diff --git a/.eslintignore b/.eslintignore index e69de29..7c5c224 100644 --- a/.eslintignore +++ b/.eslintignore @@ -0,0 +1,2 @@ +dist +*.js \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index c7da300..d66114d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,13 +4,17 @@ "commonjs": true, "es6": true }, - "parserOptions": { - "ecmaVersion": 2020 - }, + "parser": "@typescript-eslint/parser", "globals": { "BigInt": true }, - "extends": "eslint:recommended", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "plugins": [ + "@typescript-eslint" + ], "rules": { "require-atomic-updates": "warn", "no-case-declarations": "off", @@ -20,6 +24,13 @@ "no-global-assign": "off", "prefer-const": "error", "no-var": "error", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "no-extra-semi": "off", + "@typescript-eslint/no-extra-semi": "error", + "@typescript-eslint/no-empty-interface": "warn", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/explicit-function-return-type": "error", "one-var": [ "error", "never" diff --git a/.gitignore b/.gitignore index ce5d611..bd3afc3 100644 --- a/.gitignore +++ b/.gitignore @@ -58,10 +58,7 @@ typings/ .env # custom -sign.js -t.js -p.js config.json certs -/cdn -dump.rdb \ No newline at end of file +cdn +dist \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 79df13e..91b3c7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "account", - "version": "1.0.0", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "account", - "version": "1.0.0", + "version": "2.0.0", "license": "ISC", "dependencies": { "aws-sdk": "^2.978.0", @@ -17,38 +17,1450 @@ "dotenv": "^16.0.3", "email-validator": "^2.0.4", "express": "^4.17.1", - "express-form-data": "^2.0.17", - "express-rate-limit": "^5.3.0", + "express-rate-limit": "^6.7.0", "express-subdomain": "^1.0.5", - "formidable": "^1.2.2", "fs-extra": "^8.1.0", "got": "^11.8.2", "hcaptcha": "^0.1.0", "image-pixels": "^1.1.1", - "joi": "^17.6.1", - "kaitai-struct": "^0.9.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", + "joi": "^17.8.3", "mii-js": "github:PretendoNetwork/mii-js", - "moment": "^2.24.0", - "moment-timezone": "^0.5.27", - "mongoose": "^5.8.3", - "mongoose-unique-validator": "^2.0.3", + "moment": "^2.29.4", + "mongoose": "^7.0.0", + "mongoose-unique-validator": "github:stenneepro/mongoose-unique-validator#bump/mongoose7", "morgan": "^1.9.1", - "multer": "^1.4.3", + "node-rsa": "^1.0.7", "nodemailer": "^6.4.2", "redis": "^4.3.1", "tga": "^1.0.4", + "typescript-is": "^0.19.0", "validator": "^13.7.0", "xmlbuilder": "^13.0.2", "xmlbuilder2": "0.0.4" }, "devDependencies": { - "node-rsa": "^1.0.7", + "@hcaptcha/types": "^1.0.3", + "@types/bcrypt": "^5.0.0", + "@types/cors": "^2.8.13", + "@types/dicer": "^0.2.2", + "@types/express": "^4.17.17", + "@types/fs-extra": "^11.0.1", + "@types/mongoose-unique-validator": "^1.0.7", + "@types/morgan": "^1.9.4", + "@types/ndarray": "^1.0.11", + "@types/node": "^18.14.4", + "@types/node-rsa": "^1.1.1", + "@types/nodemailer": "^6.4.7", + "@types/qs": "^6.9.7", + "@types/validator": "^13.7.14", + "@types/w3c-css-typed-object-model-level-1": "^20180410.0.5", + "@typescript-eslint/eslint-plugin": "^5.54.1", + "@typescript-eslint/parser": "^5.54.1", + "eslint": "^8.35.0", + "ndarray": "^1.0.19", "prompt": "^1.0.0", + "typescript": "^4.9.5", "yesno": "^0.4.0" } }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "optional": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "optional": true, + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "optional": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "optional": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/abort-controller": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.292.0.tgz", + "integrity": "sha512-lf+OPptL01kvryIJy7+dvFux5KbJ6OTwLPPEekVKZ2AfEvwcVtOZWFUhyw3PJCBTVncjKB1Kjl3V/eTS3YuPXQ==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/abort-controller/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.294.0.tgz", + "integrity": "sha512-QMk/QratNvAvmnJ77pu9KDjpfUf/5LOJplHcLKcY962ewJGBtFFY4XZVnmUgQMSG0phRODtex7pyH//FueGKLQ==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.294.0", + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/credential-provider-node": "3.294.0", + "@aws-sdk/fetch-http-handler": "3.292.0", + "@aws-sdk/hash-node": "3.292.0", + "@aws-sdk/invalid-dependency": "3.292.0", + "@aws-sdk/middleware-content-length": "3.292.0", + "@aws-sdk/middleware-endpoint": "3.292.0", + "@aws-sdk/middleware-host-header": "3.292.0", + "@aws-sdk/middleware-logger": "3.292.0", + "@aws-sdk/middleware-recursion-detection": "3.292.0", + "@aws-sdk/middleware-retry": "3.293.0", + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/middleware-signing": "3.292.0", + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/middleware-user-agent": "3.293.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/node-http-handler": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/smithy-client": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "@aws-sdk/util-body-length-browser": "3.292.0", + "@aws-sdk/util-body-length-node": "3.292.0", + "@aws-sdk/util-defaults-mode-browser": "3.292.0", + "@aws-sdk/util-defaults-mode-node": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "@aws-sdk/util-retry": "3.292.0", + "@aws-sdk/util-user-agent-browser": "3.292.0", + "@aws-sdk/util-user-agent-node": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.294.0.tgz", + "integrity": "sha512-+FuxQTi5WvnaXM5JbNLkBIzQ3An4gA0ox61N1u+3xled+nywKb1yQ7WmRpyMG5bLbkmnj3aqoo5/uskFc4c4EA==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/fetch-http-handler": "3.292.0", + "@aws-sdk/hash-node": "3.292.0", + "@aws-sdk/invalid-dependency": "3.292.0", + "@aws-sdk/middleware-content-length": "3.292.0", + "@aws-sdk/middleware-endpoint": "3.292.0", + "@aws-sdk/middleware-host-header": "3.292.0", + "@aws-sdk/middleware-logger": "3.292.0", + "@aws-sdk/middleware-recursion-detection": "3.292.0", + "@aws-sdk/middleware-retry": "3.293.0", + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/middleware-user-agent": "3.293.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/node-http-handler": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/smithy-client": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "@aws-sdk/util-body-length-browser": "3.292.0", + "@aws-sdk/util-body-length-node": "3.292.0", + "@aws-sdk/util-defaults-mode-browser": "3.292.0", + "@aws-sdk/util-defaults-mode-node": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "@aws-sdk/util-retry": "3.292.0", + "@aws-sdk/util-user-agent-browser": "3.292.0", + "@aws-sdk/util-user-agent-node": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.294.0.tgz", + "integrity": "sha512-/ZfDud76MdSPJ/TxjV2xLE30XbBQDZwKQ32axwoK1eziPvrAIUBYVgpBwj+m0quhoiQhBKkg3aFl6j39AF2thw==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/fetch-http-handler": "3.292.0", + "@aws-sdk/hash-node": "3.292.0", + "@aws-sdk/invalid-dependency": "3.292.0", + "@aws-sdk/middleware-content-length": "3.292.0", + "@aws-sdk/middleware-endpoint": "3.292.0", + "@aws-sdk/middleware-host-header": "3.292.0", + "@aws-sdk/middleware-logger": "3.292.0", + "@aws-sdk/middleware-recursion-detection": "3.292.0", + "@aws-sdk/middleware-retry": "3.293.0", + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/middleware-user-agent": "3.293.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/node-http-handler": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/smithy-client": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "@aws-sdk/util-body-length-browser": "3.292.0", + "@aws-sdk/util-body-length-node": "3.292.0", + "@aws-sdk/util-defaults-mode-browser": "3.292.0", + "@aws-sdk/util-defaults-mode-node": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "@aws-sdk/util-retry": "3.292.0", + "@aws-sdk/util-user-agent-browser": "3.292.0", + "@aws-sdk/util-user-agent-node": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/client-sso/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.294.0.tgz", + "integrity": "sha512-AefqwhFjTDzelZuSYhriJbiI+GQwf2yKiKAnCt0gRj6rswewStM63Gtlhfb01sFPp+ZiqPcyQ47LqUaHp1mz/g==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/credential-provider-node": "3.294.0", + "@aws-sdk/fetch-http-handler": "3.292.0", + "@aws-sdk/hash-node": "3.292.0", + "@aws-sdk/invalid-dependency": "3.292.0", + "@aws-sdk/middleware-content-length": "3.292.0", + "@aws-sdk/middleware-endpoint": "3.292.0", + "@aws-sdk/middleware-host-header": "3.292.0", + "@aws-sdk/middleware-logger": "3.292.0", + "@aws-sdk/middleware-recursion-detection": "3.292.0", + "@aws-sdk/middleware-retry": "3.293.0", + "@aws-sdk/middleware-sdk-sts": "3.292.0", + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/middleware-signing": "3.292.0", + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/middleware-user-agent": "3.293.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/node-http-handler": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/smithy-client": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "@aws-sdk/util-body-length-browser": "3.292.0", + "@aws-sdk/util-body-length-node": "3.292.0", + "@aws-sdk/util-defaults-mode-browser": "3.292.0", + "@aws-sdk/util-defaults-mode-node": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "@aws-sdk/util-retry": "3.292.0", + "@aws-sdk/util-user-agent-browser": "3.292.0", + "@aws-sdk/util-user-agent-node": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/config-resolver": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.292.0.tgz", + "integrity": "sha512-cB3twnNR7vYvlt2jvw8VlA1+iv/tVzl+/S39MKqw2tepU+AbJAM0EHwb/dkf1OKSmlrnANXhshx80MHF9zL4mA==", + "optional": true, + "dependencies": { + "@aws-sdk/signature-v4": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-config-provider": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/config-resolver/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.294.0.tgz", + "integrity": "sha512-YSPqHEbLC0dnbFF5LdlMH0B50sMSN/CyG/sHkPYUwL/hkbUk9URnVW7ZJlt6lRftT7X4C7muzLqUP8sJAaiJEA==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.294.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.292.0.tgz", + "integrity": "sha512-YbafSG0ZEKE2969CJWVtUhh3hfOeLPecFVoXOtegCyAJgY5Ghtu4TsVhL4DgiGAgOC30ojAmUVQEXzd7xJF5xA==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-imds": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.292.0.tgz", + "integrity": "sha512-W/peOgDSRYulgzFpUhvgi1pCm6piBz6xrVN17N4QOy+3NHBXRVMVzYk6ct2qpLPgJUSEZkcpP+Gds+bBm8ed1A==", + "optional": true, + "dependencies": { + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-imds/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.294.0.tgz", + "integrity": "sha512-pdTPbaAb5bWA+DnuKoL2TpXeNDp6Ejpv/OYt+bw2gdzl9w5r/ZCtUTTbW+Vvejr4WL5s3c1bY96kwdqCn7iLqA==", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.292.0", + "@aws-sdk/credential-provider-imds": "3.292.0", + "@aws-sdk/credential-provider-process": "3.292.0", + "@aws-sdk/credential-provider-sso": "3.294.0", + "@aws-sdk/credential-provider-web-identity": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.294.0.tgz", + "integrity": "sha512-zUL1Qhb4BsQIZCs/TPpG4oIYH/9YsGiS+Se1tasSGjTOLfBy7jhOZ0QIdpEeyAx/EP8blOBredM9xWfEXgiHVA==", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.292.0", + "@aws-sdk/credential-provider-imds": "3.292.0", + "@aws-sdk/credential-provider-ini": "3.294.0", + "@aws-sdk/credential-provider-process": "3.292.0", + "@aws-sdk/credential-provider-sso": "3.294.0", + "@aws-sdk/credential-provider-web-identity": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.292.0.tgz", + "integrity": "sha512-CFVXuMuUvg/a4tknzRikEDwZBnKlHs1LZCpTXIGjBdUTdosoi4WNzDLzGp93ZRTtcgFz+4wirz2f7P3lC0NrQw==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.294.0.tgz", + "integrity": "sha512-UxrcAA/0l7j9+3tolYcG5M61D/IE1Bjd/9H87H1i2A2BrwUUBhW1Dp/vvROEDrrywlMDG3CDF3T/7ADtTak+sg==", + "optional": true, + "dependencies": { + "@aws-sdk/client-sso": "3.294.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/token-providers": "3.294.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.292.0.tgz", + "integrity": "sha512-4DbtIEM9gGVfqYlMdYXg3XY+vBhemjB1zXIequottW8loLYM8Vuz4/uGxxKNze6evVVzowsA0wKrYclE1aj/Rg==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.294.0.tgz", + "integrity": "sha512-CRGwCs6F31mQzjYi5YF2Z6c1T+UrFKtJSa6Ff/Q1rrPnROexyhBVnpP8WzpkODx/pZxKtTX50IX7ehIxbFDIyQ==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.294.0", + "@aws-sdk/client-sso": "3.294.0", + "@aws-sdk/client-sts": "3.294.0", + "@aws-sdk/credential-provider-cognito-identity": "3.294.0", + "@aws-sdk/credential-provider-env": "3.292.0", + "@aws-sdk/credential-provider-imds": "3.292.0", + "@aws-sdk/credential-provider-ini": "3.294.0", + "@aws-sdk/credential-provider-node": "3.294.0", + "@aws-sdk/credential-provider-process": "3.292.0", + "@aws-sdk/credential-provider-sso": "3.294.0", + "@aws-sdk/credential-provider-web-identity": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/fetch-http-handler": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.292.0.tgz", + "integrity": "sha512-zh3bhUJbL8RSa39ZKDcy+AghtUkIP8LwcNlwRIoxMQh3Row4D1s4fCq0KZCx98NJBEXoiTLyTQlZxxI//BOb1Q==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/querystring-builder": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/fetch-http-handler/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/hash-node": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.292.0.tgz", + "integrity": "sha512-1yLxmIsvE+eK36JXEgEIouTITdykQLVhsA5Oai//Lar6Ddgu1sFpLDbdkMtKbrh4I0jLN9RacNCkeVQjZPTCCQ==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-buffer-from": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/hash-node/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/invalid-dependency": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.292.0.tgz", + "integrity": "sha512-39OUV78CD3TmEbjhpt+V+Fk4wAGWhixqHxDSN8+4WL0uB4Fl7k5m3Z9hNY78AttHQSl2twR7WtLztnXPAFsriw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/invalid-dependency/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.292.0.tgz", + "integrity": "sha512-kW/G5T/fzI0sJH5foZG6XJiNCevXqKLxV50qIT4B1pMuw7regd4ALIy0HwSqj1nnn9mSbRWBfmby0jWCJsMcwg==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/is-array-buffer/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-content-length": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.292.0.tgz", + "integrity": "sha512-2gMWzQus5mj14menolpPDbYBeaOYcj7KNFZOjTjjI3iQ0KqyetG6XasirNrcJ/8QX1BRmpTol8Xjp2Ue3Gbzwg==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-content-length/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-endpoint": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.292.0.tgz", + "integrity": "sha512-cPMkiSxpZGG6tYlW4OS+ucS6r43f9ddX9kcUoemJCY10MOuogdPjulCAjE0HTs2PLKSOrrG4CTP4Q4wWDrH4Bw==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/signature-v4": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-config-provider": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.292.0.tgz", + "integrity": "sha512-mHuCWe3Yg2S5YZ7mB7sKU6C97XspfqrimWjMW9pfV2usAvLA3R0HrB03jpR5vpZ3P4q7HB6wK3S6CjYMGGRNag==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.292.0.tgz", + "integrity": "sha512-yZNY1XYmG3NG+uonET7jzKXNiwu61xm/ZZ6i/l51SusuaYN+qQtTAhOFsieQqTehF9kP4FzbsWgPDwD8ZZX9lw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.292.0.tgz", + "integrity": "sha512-kA3VZpPko0Zqd7CYPTKAxhjEv0HJqFu2054L04dde1JLr43ro+2MTdX7vsHzeAFUVRphqatFFofCumvXmU6Mig==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-retry": { + "version": "3.293.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.293.0.tgz", + "integrity": "sha512-7tiaz2GzRecNHaZ6YnF+Nrtk3au8qF6oiipf11R7MJiqJ0fkMLnz/iRrlakDziS9qF/a9v+3yxb4W4NHK3f4Tw==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/service-error-classification": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "@aws-sdk/util-retry": "3.292.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-retry/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-retry/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.292.0.tgz", + "integrity": "sha512-GN5ZHEqXZqDi+HkVbaXRX9HaW/vA5rikYpWKYsmxTUZ7fB7ijvEO3co3lleJv2C+iGYRtUIHC4wYNB5xgoTCxg==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/signature-v4": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-serde": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.292.0.tgz", + "integrity": "sha512-6hN9mTQwSvV8EcGvtXbS/MpK7WMCokUku5Wu7X24UwCNMVkoRHLIkYcxHcvBTwttuOU0d8hph1/lIX4dkLwkQw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-serde/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.292.0.tgz", + "integrity": "sha512-GVfoSjDjEQ4TaO6x9MffyP3uRV+2KcS5FtexLCYOM9pJcnE9tqq9FJOrZ1xl1g+YjUVKxo4x8lu3tpEtIb17qg==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/signature-v4": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.292.0.tgz", + "integrity": "sha512-WdQpRkuMysrEwrkByCM1qCn2PPpFGGQ2iXqaFha5RzCdZDlxJni9cVNb6HzWUcgjLEYVTXCmOR9Wxm3CNW44Qg==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-stack/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.293.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.293.0.tgz", + "integrity": "sha512-gZ7/e6XwpKk9mvgA78q4Ffc796jTn02TUKx2qMDnkLVbeJXBNN2jnvYEKq8v70+o7fd/ALRudg8gBDmkkhM/Hw==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/node-config-provider": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.292.0.tgz", + "integrity": "sha512-S3NnC9dQ5GIbJYSDIldZb4zdpCOEua1tM7bjYL3VS5uqCEM93kIi/o/UkIUveMp/eqTS2LJa5HjNIz5Te6je0A==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-config-provider/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/node-http-handler": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.292.0.tgz", + "integrity": "sha512-L/E3UDSwXLXjt1XWWh0RBD55F+aZI1AEdPwdES9i1PjnZLyuxuDhEDptVibNN56+I9/4Q3SbmuVRVlOD0uzBag==", + "optional": true, + "dependencies": { + "@aws-sdk/abort-controller": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/querystring-builder": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-http-handler/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/property-provider": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.292.0.tgz", + "integrity": "sha512-dHArSvsiqhno/g55N815gXmAMrmN8DP7OeFNqJ4wJG42xsF2PFN3DAsjIuHuXMwu+7A3R1LHqIpvv0hA9KeoJQ==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/property-provider/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.292.0.tgz", + "integrity": "sha512-NLi4fq3k41aXIh1I97yX0JTy+3p6aW1NdwFwdMa674z86QNfb4SfRQRZBQe9wEnAZ/eWHVnlKIuII+U1URk/Kg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/querystring-builder": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.292.0.tgz", + "integrity": "sha512-XElIFJaReIm24eEvBtV2dOtZvcm3gXsGu/ftG8MLJKbKXFKpAP1q+K6En0Bs7/T88voKghKdKpKT+eZUWgTqlg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-uri-escape": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-builder/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/querystring-parser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.292.0.tgz", + "integrity": "sha512-iTYpYo7a8X9RxiPbjjewIpm6XQPx2EOcF1dWCPRII9EFlmZ4bwnX+PDI36fIo9oVs8TIKXmwNGODU9nsg7CSAw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-parser/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/service-error-classification": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.292.0.tgz", + "integrity": "sha512-X1k3sixCeC45XSNHBe+kRBQBwPDyTFtFITb8O5Qw4dS9XWGhrUJT4CX0qE5aj8qP3F9U5nRizs9c2mBVVP0Caw==", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/shared-ini-file-loader": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.292.0.tgz", + "integrity": "sha512-Av2TTYg1Jig2kbkD56ybiqZJB6vVrYjv1W5UQwY/q3nA/T2mcrgQ20ByCOt5Bv9VvY7FSgC+znj+L4a7RLGmBg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/shared-ini-file-loader/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/signature-v4": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.292.0.tgz", + "integrity": "sha512-+rw47VY5mvBecn13tDQTl1ipGWg5tE63faWgmZe68HoBL87ZiDzsd7bUKOvjfW21iMgWlwAppkaNNQayYRb2zg==", + "optional": true, + "dependencies": { + "@aws-sdk/is-array-buffer": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-hex-encoding": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "@aws-sdk/util-uri-escape": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/smithy-client": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.292.0.tgz", + "integrity": "sha512-S8PKzjPkZ6SXYZuZiU787dMsvQ0d/LFEhw2OI4Oe2An9Fc2IwJ2FYukyHoQJOV2tV0DiuMebPo7eMyQyjKElvA==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/smithy-client/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.294.0.tgz", + "integrity": "sha512-6nwO04LtC5f4AsUvGZXyjaswuEK4Rr2VsuANpMKrPCgunRfI58a8YXLniudOSXN6e7CFJ6M3uo/h5YXqtnzGug==", + "optional": true, + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.294.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/types": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.292.0.tgz", + "integrity": "sha512-1teYAY2M73UXZxMAxqZxVS2qwXjQh0OWtt7qyLfha0TtIk/fZ1hRwFgxbDCHUFcdNBSOSbKH/ESor90KROXLCQ==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/url-parser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.292.0.tgz", + "integrity": "sha512-NZeAuZCk1x6TIiWuRfbOU6wHPBhf0ly2qOHzWut4BCH+b4RrDmFF8EmXcH1auEfGhE7yRyR6XqIN0t3S+hYACA==", + "optional": true, + "dependencies": { + "@aws-sdk/querystring-parser": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/url-parser/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-base64": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.292.0.tgz", + "integrity": "sha512-zjNCwNdy617yFvEjZorepNWXB2sQCVfsShCwFy/kIQ5iW5tT2jQKaqc0K77diU9atkooxw9p1W9m9sOgrkOFNw==", + "optional": true, + "dependencies": { + "@aws-sdk/util-buffer-from": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-base64/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-body-length-browser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.292.0.tgz", + "integrity": "sha512-Wd/BM+JsMiKvKs/bN3z6TredVEHh2pKudGfg3CSjTRpqFpOG903KDfyHBD42yg5PuCHoHoewJvTPKwgn7/vhaw==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-body-length-browser/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-body-length-node": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.292.0.tgz", + "integrity": "sha512-BBgipZ2P6RhogWE/qj0oqpdlyd3iSBYmb+aD/TBXwB2lA/X8A99GxweBd/kp06AmcJRoMS9WIXgbWkiiBlRlSA==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-body-length-node/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.292.0.tgz", + "integrity": "sha512-RxNZjLoXNxHconH9TYsk5RaEBjSgTtozHeyIdacaHPj5vlQKi4hgL2hIfKeeNiAfQEVjaUFF29lv81xpNMzVMQ==", + "optional": true, + "dependencies": { + "@aws-sdk/is-array-buffer": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-buffer-from/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-config-provider": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.292.0.tgz", + "integrity": "sha512-t3noYll6bPRSxeeNNEkC5czVjAiTPcsq00OwfJ2xyUqmquhLEfLwoJKmrT1uP7DjIEXdUtfoIQ2jWiIVm/oO5A==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-config-provider/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-defaults-mode-browser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.292.0.tgz", + "integrity": "sha512-7+zVUlMGfa8/KT++9humHo6IDxTnxMCmWUj5jVNlkpk6h7Ecmppf7aXotviyVIA43lhtz0p2AErs0N0ekEUK+w==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-browser/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-defaults-mode-node": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.292.0.tgz", + "integrity": "sha512-SSIw85eF4BVs0fOJRyshT+R3b/UmBPhiVKCUZm2rq6+lIGkDPiSwQU3d/80AhXtiL5SFT/IzAKKgQd8qMa7q3A==", + "optional": true, + "dependencies": { + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/credential-provider-imds": "3.292.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-node/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.293.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.293.0.tgz", + "integrity": "sha512-R/99aNV49Refpv5guiUjEUrZYlvnfaNBniB+/ZtMO3ixxUopapssCrUivuJrmhccmrYaTCZw7dRzIWjU1jJhKg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.292.0.tgz", + "integrity": "sha512-qBd5KFIUywQ3qSSbj814S2srk0vfv8A6QMI+Obs1y2LHZFdQN5zViptI4UhXhKOHe+NnrHWxSuLC/LMH6q3SmA==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-hex-encoding/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.292.0.tgz", + "integrity": "sha512-6xnFJXZI9pKw5lQCDvuWA5PnOaUtNRKWwdxvGkkLx5orboFaoVMS6zowjSQxwVNRjW82u6dYNkhmj9mZ8VSjWg==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-middleware": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.292.0.tgz", + "integrity": "sha512-KjhS7flfoBKDxbiBZjLjMvEizXgjfQb7GQEItgzGoI9rfGCmZtvqCcqQQoIlxb8bIzGRggAUHtBGWnlLbpb+GQ==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-middleware/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-retry": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.292.0.tgz", + "integrity": "sha512-JEHyF7MpVeRF5uR4LDYgpOKcFpOPiAj8TqN46SVOQQcL1K+V7cSr7O7N7J6MwJaN9XOzAcBadeIupMm7/BFbgw==", + "optional": true, + "dependencies": { + "@aws-sdk/service-error-classification": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/util-retry/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.292.0.tgz", + "integrity": "sha512-hOQtUMQ4VcQ9iwKz50AoCp1XBD5gJ9nly/gJZccAM7zSA5mOO8RRKkbdonqquVHxrO0CnYgiFeCh3V35GFecUw==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-uri-escape/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.292.0.tgz", + "integrity": "sha512-dld+lpC3QdmTQHdBWJ0WFDkXDSrJgfz03q6mQ8+7H+BC12ZhT0I0g9iuvUjolqy7QR00OxOy47Y9FVhq8EC0Gg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.292.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.292.0.tgz", + "integrity": "sha512-f+NfIMal5E61MDc5WGhUEoicr7b1eNNhA+GgVdSB/Hg5fYhEZvFK9RZizH5rrtsLjjgcr9nPYSR7/nDKCJLumw==", + "optional": true, + "dependencies": { + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-user-agent-node/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-utf8": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.292.0.tgz", + "integrity": "sha512-FPkj+Z59/DQWvoVu2wFaRncc3KVwe/pgK3MfVb0Lx+Ibey5KUx+sNpJmYcVYHUAe/Nv/JeIpOtYuC96IXOnI6w==", + "optional": true, + "dependencies": { + "@aws-sdk/util-buffer-from": "3.292.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-utf8-browser/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/@aws-sdk/util-utf8/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -58,6 +1470,61 @@ "node": ">=0.1.90" } }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", + "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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/@eslint/js": { + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", + "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -71,6 +1538,68 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@hcaptcha/types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@hcaptcha/types/-/types-1.0.3.tgz", + "integrity": "sha512-1mbU6eSGawRrqeahRrOzZo/SVLI6oZ5/azuBpSyVrRRR96CnS3fOVDWfzxpngfxKD0/I9Rwu6c/3ITqD8rXeTQ==", + "dev": true + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/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/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", @@ -90,6 +1619,41 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@oozcitak/dom": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-0.0.11.tgz", @@ -215,9 +1779,9 @@ } }, "node_modules/@redis/client": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.5.tgz", - "integrity": "sha512-fuMnpDYSjT5JXR9rrCW1YWA4L8N/9/uS4ImT3ZEC/hcaQRI1D/9FvwjriRj1UvepIgzZXthFVKMNRzP/LNL7BQ==", + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.6.tgz", + "integrity": "sha512-dFD1S6je+A47Lj22jN/upVU2fj4huR7S9APd7/ziUXsIXDL+11GPYti4Suv5y8FuXaN+0ZG4JF+y1houEJ7ToA==", "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -244,9 +1808,9 @@ } }, "node_modules/@redis/search": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.1.tgz", - "integrity": "sha512-pqCXTc5e7wJJgUuJiC3hBgfoFRoPxYzwn0BEfKgejTM7M/9zP3IpUcqcjgfp8hF+LoV8rHZzcNTz7V+pEIY7LQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz", + "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==", "peerDependencies": { "@redis/client": "^1.0.0" } @@ -299,14 +1863,25 @@ "node": ">=10" } }, - "node_modules/@types/bson": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz", - "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==", + "node_modules/@types/bcrypt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "dev": true, "dependencies": { "@types/node": "*" } }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -318,11 +1893,86 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/dicer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@types/dicer/-/dicer-0.2.2.tgz", + "integrity": "sha512-UPLqCYey+jn5Mf57KFDwxD/7VZYDsbYUi3iyTehLFVjlbvl/JcUTPaot8uKNYLO0EoZpey+rC/s5AF3VxfeC2Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.1.tgz", + "integrity": "sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==", + "dev": true, + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/jsonfile": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.1.tgz", + "integrity": "sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -331,19 +1981,187 @@ "@types/node": "*" } }, - "node_modules/@types/mongodb": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", - "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "node_modules/@types/mongoose-unique-validator": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/mongoose-unique-validator/-/mongoose-unique-validator-1.0.7.tgz", + "integrity": "sha512-wgPWN7x9mHdy8x1jcluDF7EDWFb/s8LD7skLr012C8i2Q3tUhdTlp3JxRhFbEM3TD7J1INaoTrWaSaguhuqilQ==", + "dev": true, + "dependencies": { + "mongoose": "^6.3.0" + } + }, + "node_modules/@types/mongoose-unique-validator/node_modules/bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "dev": true, + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@types/mongoose-unique-validator/node_modules/buffer": { + "version": "5.7.1", + "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" + } + }, + "node_modules/@types/mongoose-unique-validator/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@types/mongoose-unique-validator/node_modules/debug/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/@types/mongoose-unique-validator/node_modules/mongodb": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.14.0.tgz", + "integrity": "sha512-coGKkWXIBczZPr284tYKFLg+KbGPPLlSbdgfKAb6QqCFt5bo5VFZ50O3FFzsw4rnkqjwT6D8Qcoo9nshYKM7Mg==", + "dev": true, + "dependencies": { + "bson": "^4.7.0", + "mongodb-connection-string-url": "^2.5.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "saslprep": "^1.0.3" + } + }, + "node_modules/@types/mongoose-unique-validator/node_modules/mongoose": { + "version": "6.10.5", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.10.5.tgz", + "integrity": "sha512-y4HL4/9EySec7L0gJ+pCm9heLSF45uIIvRS4fSeAFWDfe4vXW1vRZJwTz7OGkra3ZoSfRnFTo9bNZkuggDVlVA==", + "dev": true, + "dependencies": { + "bson": "^4.7.0", + "kareem": "2.5.1", + "mongodb": "4.14.0", + "mpath": "0.9.0", + "mquery": "4.0.3", + "ms": "2.1.3", + "sift": "16.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/@types/mongoose-unique-validator/node_modules/mquery": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", + "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", + "dev": true, + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@types/mongoose-unique-validator/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==", + "dev": true + }, + "node_modules/@types/morgan": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz", + "integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==", + "dev": true, "dependencies": { - "@types/bson": "*", "@types/node": "*" } }, + "node_modules/@types/ndarray": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.11.tgz", + "integrity": "sha512-hOZVTN24zDHwCHaW7mF9n1vHJt83fZhNZ0YYRBwQGhA96yBWWDPTDDlqJatagHIOJB0a4xoNkNc+t/Cxd+6qUA==", + "dev": true + }, "node_modules/@types/node": { - "version": "18.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", - "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" + "version": "18.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.6.tgz", + "integrity": "sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==" + }, + "node_modules/@types/node-rsa": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/node-rsa/-/node-rsa-1.1.1.tgz", + "integrity": "sha512-itzxtaBgk4OMbrCawVCvas934waMZWjW17v7EYgFVlfYS/cl0/P7KZdojWCq9SDJMI5cnLQLUP8ayhVCTY8TEg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/nodemailer": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.7.tgz", + "integrity": "sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true }, "node_modules/@types/responselike": { "version": "1.0.0", @@ -353,6 +2171,341 @@ "@types/node": "*" } }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "node_modules/@types/serve-static": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/validator": { + "version": "13.7.14", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.14.tgz", + "integrity": "sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g==", + "dev": true + }, + "node_modules/@types/w3c-css-typed-object-model-level-1": { + "version": "20180410.0.5", + "resolved": "https://registry.npmjs.org/@types/w3c-css-typed-object-model-level-1/-/w3c-css-typed-object-model-level-1-20180410.0.5.tgz", + "integrity": "sha512-khoqDQqA/fUdK6mEJtQEfMAPCKkk8E1D+ahM/0HCy3mc5L33wJinqQNXu9Uf4+mqzMvORljHg76jvrmwS0bEjA==", + "dev": true + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz", + "integrity": "sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/type-utils": "5.54.1", + "@typescript-eslint/utils": "5.54.1", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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/parser": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz", + "integrity": "sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/typescript-estree": "5.54.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz", + "integrity": "sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/visitor-keys": "5.54.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz", + "integrity": "sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.54.1", + "@typescript-eslint/utils": "5.54.1", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/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/types": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.1.tgz", + "integrity": "sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz", + "integrity": "sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/visitor-keys": "5.54.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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/utils": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.1.tgz", + "integrity": "sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/typescript-estree": "5.54.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz", + "integrity": "sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.54.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -381,6 +2534,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-node": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", @@ -454,10 +2616,20 @@ "node": ">=8" } }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, "node_modules/aproba": { "version": "2.0.0", @@ -477,9 +2649,9 @@ } }, "node_modules/are-we-there-yet/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": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -497,6 +2669,12 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", @@ -515,6 +2693,15 @@ "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==" }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/arraybuffer-to-string": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/arraybuffer-to-string/-/arraybuffer-to-string-1.0.2.tgz", @@ -547,14 +2734,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/atob-lite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", @@ -572,9 +2751,9 @@ } }, "node_modules/aws-sdk": { - "version": "2.1313.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1313.0.tgz", - "integrity": "sha512-8GMdtV2Uch3HL2c6+P3lNZFTcg/fqq9L3EWYRLb6ljCZvWKTssjdkjSJFDyTReNgeiKV224YRPYQbKpOEz4flQ==", + "version": "2.1328.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1328.0.tgz", + "integrity": "sha512-ud8ieE+hGX/cWHkQ9kMxQw6w+onbv71PDrcPmP2j8cmYv5IPlM5Zh8/tpsmXApLYDmQMuZ3TtssiB1KmoSbzgA==", "dependencies": { "buffer": "4.9.2", "events": "1.1.1", @@ -670,47 +2849,6 @@ "resolved": "https://registry.npmjs.org/bit-buffer/-/bit-buffer-0.2.5.tgz", "integrity": "sha512-x1yGnmXvFg6e3DiyRztElbcn1bsCTFSoM/ncAzY62uE0JdTl5xlKJd0ooqLYoPbhdsnpehSIQrdIvclcZJYwiA==" }, - "node_modules/bl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", - "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/bl/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/bl/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/bl/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/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/bmp-js": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", @@ -739,6 +2877,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "optional": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -748,6 +2892,18 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/brfs": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brfs/-/brfs-2.0.2.tgz", @@ -763,11 +2919,11 @@ } }, "node_modules/bson": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", - "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-5.0.1.tgz", + "integrity": "sha512-y09gBGusgHtinMon/GVbv1J6FrXhnr/+6hqLlSmEFzkz6PodqF6TxjyvfvY3AfO+oG1mgUtbC86xSbOlwvM62Q==", "engines": { - "node": ">=0.6.19" + "node": ">=14.20.1" } }, "node_modules/buffer": { @@ -803,18 +2959,6 @@ "resolved": "https://registry.npmjs.org/buffer-to-uint8array/-/buffer-to-uint8array-1.1.0.tgz", "integrity": "sha512-JVTSbtA6YuOGdu5NL0ffizsBwuwbTXfV7OC91FhazMz9UKP/KlDS+Z7wuiSRClbnTQz52fJgVXI9YDXQRVl2sQ==" }, - "node_modules/busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==", - "dependencies": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -860,11 +3004,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -897,6 +3066,24 @@ "node": ">=0.10.0" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -944,9 +3131,9 @@ } }, "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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -970,84 +3157,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/connect-multiparty": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/connect-multiparty/-/connect-multiparty-2.2.0.tgz", - "integrity": "sha512-zKcpA7cuXGEhuw9Pz7JmVCFmp85jzGLGm/iiagXTwyEAJp4ypLPtRS/V4IGuGb9KjjrgHBs6P/gDCpZHnFzksA==", - "dependencies": { - "http-errors": "~1.7.0", - "multiparty": "~4.2.1", - "on-finished": "~2.3.0", - "qs": "~6.5.2", - "type-is": "~1.6.16" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/connect-multiparty/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/connect-multiparty/node_modules/http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/connect-multiparty/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/connect-multiparty/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/connect-multiparty/node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/connect-multiparty/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/connect-multiparty/node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -1107,6 +3216,20 @@ "node": ">= 0.10" } }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", @@ -1200,14 +3323,6 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, - "node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1245,6 +3360,30 @@ "node": ">=0.8.0" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -1270,9 +3409,9 @@ } }, "node_modules/duplexer2/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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1422,6 +3561,18 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/escodegen": { "version": "1.14.3", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", @@ -1443,6 +3594,224 @@ "source-map": "~0.6.1" } }, + "node_modules/eslint": { + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", + "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^2.0.0", + "@eslint/js": "8.35.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -1455,6 +3824,48 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", @@ -1542,56 +3953,16 @@ "node": ">= 0.10.0" } }, - "node_modules/express-form-data": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/express-form-data/-/express-form-data-2.0.19.tgz", - "integrity": "sha512-QWulRIYvZV/ZOuO0+SNpCs9Gl0st9SssYtm5Icfm17p2MU4HASLpd4K5w8fwnXB/lPPskecVSeqjVWi2SwIb1A==", - "dependencies": { - "connect-multiparty": "^2.2.0", - "fs-extra": "^9.1.0", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": ">=5.0.0" - } - }, - "node_modules/express-form-data/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/express-form-data/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/express-form-data/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/express-rate-limit": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.5.1.tgz", - "integrity": "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg==" + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz", + "integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==", + "engines": { + "node": ">= 12.9.0" + }, + "peerDependencies": { + "express": "^4 || ^5" + } }, "node_modules/express-subdomain": { "version": "1.0.5", @@ -1638,6 +4009,34 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1648,6 +4047,43 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "node_modules/fast-xml-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", + "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==", + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/file-type": { "version": "10.11.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", @@ -1656,6 +4092,18 @@ "node": ">=6" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -1673,6 +4121,41 @@ "node": ">= 0.8" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, "node_modules/flatten-vertex-data": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz", @@ -1715,15 +4198,6 @@ "node": ">= 0.12" } }, - "node_modules/formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", - "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1871,6 +4345,53 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -1911,6 +4432,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -1943,6 +4470,15 @@ "node": ">= 0.4.0" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -2073,6 +4609,15 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/image-pixels": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/image-pixels/-/image-pixels-1.1.1.tgz", @@ -2117,6 +4662,31 @@ "node": ">=6" } }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2131,6 +4701,17 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "dev": true + }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2219,6 +4800,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -2241,6 +4831,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -2287,6 +4907,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -2301,9 +4927,9 @@ } }, "node_modules/joi": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.7.1.tgz", - "integrity": "sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA==", + "version": "17.8.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.8.3.tgz", + "integrity": "sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w==", "dependencies": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", @@ -2317,6 +4943,28 @@ "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz", "integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==" }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -2337,6 +4985,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -2364,15 +5018,13 @@ "node": ">=0.6.0" } }, - "node_modules/kaitai-struct": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/kaitai-struct/-/kaitai-struct-0.9.0.tgz", - "integrity": "sha512-mfoBu9+IGqaY3ykG1TyAy9omOAZWtheqESQOvo/HKIQVTz+gRPVCNBnhjbO+8wAQ77RD33wYvLBWmITuXIviQg==" - }, "node_modules/kareem": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", - "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "engines": { + "node": ">=12.0.0" + } }, "node_modules/keyv": { "version": "4.5.2", @@ -2394,6 +5046,21 @@ "node": ">= 0.8.0" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -2410,10 +5077,10 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, - "node_modules/lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==" + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/lowercase-keys": { "version": "2.0.0", @@ -2499,6 +5166,15 @@ "node": ">=0.10.0" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -2507,6 +5183,19 @@ "node": ">= 0.6" } }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mii-js": { "version": "1.0.4", "resolved": "git+ssh://git@github.com/PretendoNetwork/mii-js.git#5d8eb8013514a13b0df6eb4a5bfd8b5a63fb9861", @@ -2573,9 +5262,9 @@ } }, "node_modules/minipass": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.3.tgz", - "integrity": "sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz", + "integrity": "sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==", "engines": { "node": ">=8" } @@ -2604,14 +5293,14 @@ } }, "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "bin": { "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/moment": { @@ -2622,118 +5311,85 @@ "node": "*" } }, - "node_modules/moment-timezone": { - "version": "0.5.40", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz", - "integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==", - "dependencies": { - "moment": ">= 2.9.0" - }, - "engines": { - "node": "*" - } - }, "node_modules/mongodb": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz", - "integrity": "sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.1.0.tgz", + "integrity": "sha512-qgKb7y+EI90y4weY3z5+lIgm8wmexbonz0GalHkSElQXVKtRuwqXuhXKccyvIjXCJVy9qPV82zsinY0W1FBnJw==", "dependencies": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "optional-require": "^1.1.8", - "safe-buffer": "^5.1.2" + "bson": "^5.0.1", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" }, "engines": { - "node": ">=4" + "node": ">=14.20.1" }, "optionalDependencies": { - "saslprep": "^1.0.0" + "saslprep": "^1.0.3" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.201.0", + "mongodb-client-encryption": "^2.3.0", + "snappy": "^7.2.2" }, "peerDependenciesMeta": { - "aws4": { - "optional": true - }, - "bson-ext": { - "optional": true - }, - "kerberos": { + "@aws-sdk/credential-providers": { "optional": true }, "mongodb-client-encryption": { "optional": true }, - "mongodb-extjson": { - "optional": true - }, "snappy": { "optional": true } } }, - "node_modules/mongodb/node_modules/optional-require": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", - "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", "dependencies": { - "require-at": "^1.0.6" - }, - "engines": { - "node": ">=4" + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" } }, "node_modules/mongoose": { - "version": "5.13.15", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.15.tgz", - "integrity": "sha512-cxp1Gbb8yUWkaEbajdhspSaKzAvsIvOtRlYD87GN/P2QEUhpd6bIvebi36T6M0tIVAMauNaK9SPA055N3PwF8Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.0.0.tgz", + "integrity": "sha512-U0YPURDld+k/nvvSG1mRClQSjZMRXwQKSU5yb9PslRnOmVz0UlBD7SjSnjUuGT0yk+7BH+kJNimsKqMxYAKkMA==", "dependencies": { - "@types/bson": "1.x || 4.0.x", - "@types/mongodb": "^3.5.27", - "bson": "^1.1.4", - "kareem": "2.3.2", - "mongodb": "3.7.3", - "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.8.4", - "mquery": "3.2.5", - "ms": "2.1.2", - "optional-require": "1.0.x", - "regexp-clone": "1.0.0", - "safe-buffer": "5.2.1", - "sift": "13.5.2", - "sliced": "1.0.1" + "bson": "^5.0.1", + "kareem": "2.5.1", + "mongodb": "5.1.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "16.0.1" }, "engines": { - "node": ">=4.0.0" + "node": ">=14.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-unique-validator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mongoose-unique-validator/-/mongoose-unique-validator-2.0.3.tgz", - "integrity": "sha512-3/8pmvAC1acBZS6eWKAWQUiZBlARE1wyWtjga4iQ2wDJeOfRlIKmAvTNHSZXKaAf7RCRUd7wh7as6yWAOrjpQg==", + "version": "3.1.0", + "resolved": "git+ssh://git@github.com/stenneepro/mongoose-unique-validator.git#1cd976b70ddd8e8e65fed1d9a387677713a8d177", + "license": "MIT", "dependencies": { "lodash.foreach": "^4.1.0", - "lodash.get": "^4.0.2" + "lodash.get": "^4.0.2", + "lodash.merge": "^4.6.2" }, "peerDependencies": { - "mongoose": "^5.2.1" + "mongoose": "^7.0.0" } }, "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==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/morgan": { "version": "1.10.0", @@ -2762,115 +5418,84 @@ } }, "node_modules/mpath": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", - "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", "engines": { "node": ">=4.0.0" } }, "node_modules/mquery": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", - "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", "dependencies": { - "bluebird": "3.5.1", - "debug": "3.1.0", - "regexp-clone": "^1.0.0", - "safe-buffer": "5.1.2", - "sliced": "1.0.1" + "debug": "4.x" }, "engines": { - "node": ">=4.0.0" + "node": ">=14.0.0" } }, "node_modules/mquery/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "ms": "2.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/mquery/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/mquery/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/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/multer": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", - "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", - "deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/multiparty": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.3.tgz", - "integrity": "sha512-Ak6EUJZuhGS8hJ3c2fY6UW5MbkGUPMBEGd13djUzoY/BHqV/gTuFWtC6IuVA7A2+v3yjBS6c4or50xhzTQZImQ==", - "dependencies": { - "http-errors": "~1.8.1", - "safe-buffer": "5.2.1", - "uid-safe": "2.1.5" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/multiparty/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/multiparty/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/multiparty/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "dev": true, + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -2879,6 +5504,11 @@ "node": ">= 0.6" } }, + "node_modules/nested-error-stacks": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", + "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==" + }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -2908,11 +5538,29 @@ } } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-rsa": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.1.1.tgz", "integrity": "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==", - "dev": true, "dependencies": { "asn1": "^0.2.4" } @@ -3017,14 +5665,6 @@ "wrappy": "1" } }, - "node_modules/optional-require": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", - "integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA==", - "engines": { - "node": ">=4" - } - }, "node_modules/optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -3049,6 +5689,48 @@ "node": ">=8" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-rect": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/parse-rect/-/parse-rect-1.2.0.tgz", @@ -3065,6 +5747,15 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -3073,6 +5764,15 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -3083,6 +5783,15 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -3093,6 +5802,18 @@ "resolved": "https://registry.npmjs.org/pick-by-alias/-/pick-by-alias-1.2.0.tgz", "integrity": "sha512-ESj2+eBxhGrcA1azgHs7lARG5+5iLakc/6nlfbpjcLl00HuuUOIuORhYXN4D1HfvMSKuVtFQjAlnwi1JHEeDIw==" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/pngjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", @@ -3192,6 +5913,26 @@ "node": ">=0.4.x" } }, + "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", @@ -3216,14 +5957,6 @@ "quote-stream": "bin/cmd.js" } }, - "node_modules/random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -3275,22 +6008,35 @@ "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" }, "node_modules/redis": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.4.tgz", - "integrity": "sha512-wi2tgDdQ+Q8q+PR5FLRx4QvDiWaA+PoJbrzsyFqlClN5R4LplHqN3scs/aGjE//mbz++W19SgxiEnQ27jnCRaA==", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.5.tgz", + "integrity": "sha512-O0OWA36gDQbswOdUuAhRL6mTZpHFN525HlgZgDaVNgCJIAZR3ya06NTESb0R+TUZ+BFaDpz6NnnVvoMx9meUFg==", "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.5", + "@redis/client": "1.5.6", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.1", + "@redis/search": "1.1.2", "@redis/time-series": "1.0.4" } }, - "node_modules/regexp-clone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", - "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "optional": true + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } }, "node_modules/request": { "version": "2.88.2", @@ -3340,14 +6086,6 @@ "uuid": "bin/uuid" } }, - "node_modules/require-at": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", - "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", - "engines": { - "node": ">=4" - } - }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -3369,6 +6107,15 @@ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", @@ -3385,6 +6132,16 @@ "resolved": "https://registry.npmjs.org/restructure/-/restructure-2.0.1.tgz", "integrity": "sha512-e0dOpjm5DseomnXx2M5lpdZ5zoHqF1+bqdMJUohoYVVQa7cBdnk7fdmeI6byNWP/kiME72EeTiSypTCVnpLiDg==" }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/revalidator": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", @@ -3408,6 +6165,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3534,6 +6314,27 @@ "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -3548,19 +6349,45 @@ } }, "node_modules/sift": { - "version": "13.5.2", - "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", - "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "node_modules/sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==" + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } }, "node_modules/source-map": { "version": "0.6.1", @@ -3649,9 +6476,9 @@ } }, "node_modules/static-module/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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3734,6 +6561,36 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -3761,16 +6618,11 @@ "node": ">=10" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/tga": { "version": "1.0.7", @@ -3794,9 +6646,9 @@ } }, "node_modules/through2/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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3830,6 +6682,18 @@ "string-to-arraybuffer": "^1.0.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -3851,9 +6715,34 @@ } }, "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "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/tunnel-agent": { "version": "0.6.0", @@ -3887,6 +6776,18 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -3904,15 +6805,34 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, - "node_modules/uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "dependencies": { - "random-bytes": "~1.0.0" + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">= 0.8" + "node": ">=4.2.0" + } + }, + "node_modules/typescript-is": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/typescript-is/-/typescript-is-0.19.0.tgz", + "integrity": "sha512-SAJEx2cxbQZhfOjDEjPnQJt1qRS1M3wrKbUwvsywVHWGbMgM1dcIf9gPWNDS1/dgTa/7Iexk2mmAHHsP9MeCsA==", + "dependencies": { + "nested-error-stacks": "^2", + "tsutils": "^3.17.1" + }, + "engines": { + "node": ">=6.14.4" + }, + "optionalDependencies": { + "reflect-metadata": ">=0.1.12" + }, + "peerDependencies": { + "typescript": "^4.1.5" } }, "node_modules/universalify": { @@ -4021,17 +6941,38 @@ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } }, "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/which-typed-array": { @@ -4165,15 +7106,1410 @@ "resolved": "https://registry.npmjs.org/yesno/-/yesno-0.4.0.tgz", "integrity": "sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==", "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } }, "dependencies": { + "@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "optional": true, + "requires": { + "tslib": "^1.11.1" + } + }, + "@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "optional": true, + "requires": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "optional": true, + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "optional": true, + "requires": { + "tslib": "^1.11.1" + } + }, + "@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "optional": true, + "requires": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "@aws-sdk/abort-controller": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.292.0.tgz", + "integrity": "sha512-lf+OPptL01kvryIJy7+dvFux5KbJ6OTwLPPEekVKZ2AfEvwcVtOZWFUhyw3PJCBTVncjKB1Kjl3V/eTS3YuPXQ==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/client-cognito-identity": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.294.0.tgz", + "integrity": "sha512-QMk/QratNvAvmnJ77pu9KDjpfUf/5LOJplHcLKcY962ewJGBtFFY4XZVnmUgQMSG0phRODtex7pyH//FueGKLQ==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.294.0", + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/credential-provider-node": "3.294.0", + "@aws-sdk/fetch-http-handler": "3.292.0", + "@aws-sdk/hash-node": "3.292.0", + "@aws-sdk/invalid-dependency": "3.292.0", + "@aws-sdk/middleware-content-length": "3.292.0", + "@aws-sdk/middleware-endpoint": "3.292.0", + "@aws-sdk/middleware-host-header": "3.292.0", + "@aws-sdk/middleware-logger": "3.292.0", + "@aws-sdk/middleware-recursion-detection": "3.292.0", + "@aws-sdk/middleware-retry": "3.293.0", + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/middleware-signing": "3.292.0", + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/middleware-user-agent": "3.293.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/node-http-handler": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/smithy-client": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "@aws-sdk/util-body-length-browser": "3.292.0", + "@aws-sdk/util-body-length-node": "3.292.0", + "@aws-sdk/util-defaults-mode-browser": "3.292.0", + "@aws-sdk/util-defaults-mode-node": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "@aws-sdk/util-retry": "3.292.0", + "@aws-sdk/util-user-agent-browser": "3.292.0", + "@aws-sdk/util-user-agent-node": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/client-sso": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.294.0.tgz", + "integrity": "sha512-+FuxQTi5WvnaXM5JbNLkBIzQ3An4gA0ox61N1u+3xled+nywKb1yQ7WmRpyMG5bLbkmnj3aqoo5/uskFc4c4EA==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/fetch-http-handler": "3.292.0", + "@aws-sdk/hash-node": "3.292.0", + "@aws-sdk/invalid-dependency": "3.292.0", + "@aws-sdk/middleware-content-length": "3.292.0", + "@aws-sdk/middleware-endpoint": "3.292.0", + "@aws-sdk/middleware-host-header": "3.292.0", + "@aws-sdk/middleware-logger": "3.292.0", + "@aws-sdk/middleware-recursion-detection": "3.292.0", + "@aws-sdk/middleware-retry": "3.293.0", + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/middleware-user-agent": "3.293.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/node-http-handler": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/smithy-client": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "@aws-sdk/util-body-length-browser": "3.292.0", + "@aws-sdk/util-body-length-node": "3.292.0", + "@aws-sdk/util-defaults-mode-browser": "3.292.0", + "@aws-sdk/util-defaults-mode-node": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "@aws-sdk/util-retry": "3.292.0", + "@aws-sdk/util-user-agent-browser": "3.292.0", + "@aws-sdk/util-user-agent-node": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/client-sso-oidc": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.294.0.tgz", + "integrity": "sha512-/ZfDud76MdSPJ/TxjV2xLE30XbBQDZwKQ32axwoK1eziPvrAIUBYVgpBwj+m0quhoiQhBKkg3aFl6j39AF2thw==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/fetch-http-handler": "3.292.0", + "@aws-sdk/hash-node": "3.292.0", + "@aws-sdk/invalid-dependency": "3.292.0", + "@aws-sdk/middleware-content-length": "3.292.0", + "@aws-sdk/middleware-endpoint": "3.292.0", + "@aws-sdk/middleware-host-header": "3.292.0", + "@aws-sdk/middleware-logger": "3.292.0", + "@aws-sdk/middleware-recursion-detection": "3.292.0", + "@aws-sdk/middleware-retry": "3.293.0", + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/middleware-user-agent": "3.293.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/node-http-handler": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/smithy-client": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "@aws-sdk/util-body-length-browser": "3.292.0", + "@aws-sdk/util-body-length-node": "3.292.0", + "@aws-sdk/util-defaults-mode-browser": "3.292.0", + "@aws-sdk/util-defaults-mode-node": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "@aws-sdk/util-retry": "3.292.0", + "@aws-sdk/util-user-agent-browser": "3.292.0", + "@aws-sdk/util-user-agent-node": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/client-sts": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.294.0.tgz", + "integrity": "sha512-AefqwhFjTDzelZuSYhriJbiI+GQwf2yKiKAnCt0gRj6rswewStM63Gtlhfb01sFPp+ZiqPcyQ47LqUaHp1mz/g==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/credential-provider-node": "3.294.0", + "@aws-sdk/fetch-http-handler": "3.292.0", + "@aws-sdk/hash-node": "3.292.0", + "@aws-sdk/invalid-dependency": "3.292.0", + "@aws-sdk/middleware-content-length": "3.292.0", + "@aws-sdk/middleware-endpoint": "3.292.0", + "@aws-sdk/middleware-host-header": "3.292.0", + "@aws-sdk/middleware-logger": "3.292.0", + "@aws-sdk/middleware-recursion-detection": "3.292.0", + "@aws-sdk/middleware-retry": "3.293.0", + "@aws-sdk/middleware-sdk-sts": "3.292.0", + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/middleware-signing": "3.292.0", + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/middleware-user-agent": "3.293.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/node-http-handler": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/smithy-client": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "@aws-sdk/util-body-length-browser": "3.292.0", + "@aws-sdk/util-body-length-node": "3.292.0", + "@aws-sdk/util-defaults-mode-browser": "3.292.0", + "@aws-sdk/util-defaults-mode-node": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "@aws-sdk/util-retry": "3.292.0", + "@aws-sdk/util-user-agent-browser": "3.292.0", + "@aws-sdk/util-user-agent-node": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/config-resolver": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.292.0.tgz", + "integrity": "sha512-cB3twnNR7vYvlt2jvw8VlA1+iv/tVzl+/S39MKqw2tepU+AbJAM0EHwb/dkf1OKSmlrnANXhshx80MHF9zL4mA==", + "optional": true, + "requires": { + "@aws-sdk/signature-v4": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-config-provider": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-cognito-identity": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.294.0.tgz", + "integrity": "sha512-YSPqHEbLC0dnbFF5LdlMH0B50sMSN/CyG/sHkPYUwL/hkbUk9URnVW7ZJlt6lRftT7X4C7muzLqUP8sJAaiJEA==", + "optional": true, + "requires": { + "@aws-sdk/client-cognito-identity": "3.294.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.292.0.tgz", + "integrity": "sha512-YbafSG0ZEKE2969CJWVtUhh3hfOeLPecFVoXOtegCyAJgY5Ghtu4TsVhL4DgiGAgOC30ojAmUVQEXzd7xJF5xA==", + "optional": true, + "requires": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-imds": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.292.0.tgz", + "integrity": "sha512-W/peOgDSRYulgzFpUhvgi1pCm6piBz6xrVN17N4QOy+3NHBXRVMVzYk6ct2qpLPgJUSEZkcpP+Gds+bBm8ed1A==", + "optional": true, + "requires": { + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.294.0.tgz", + "integrity": "sha512-pdTPbaAb5bWA+DnuKoL2TpXeNDp6Ejpv/OYt+bw2gdzl9w5r/ZCtUTTbW+Vvejr4WL5s3c1bY96kwdqCn7iLqA==", + "optional": true, + "requires": { + "@aws-sdk/credential-provider-env": "3.292.0", + "@aws-sdk/credential-provider-imds": "3.292.0", + "@aws-sdk/credential-provider-process": "3.292.0", + "@aws-sdk/credential-provider-sso": "3.294.0", + "@aws-sdk/credential-provider-web-identity": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.294.0.tgz", + "integrity": "sha512-zUL1Qhb4BsQIZCs/TPpG4oIYH/9YsGiS+Se1tasSGjTOLfBy7jhOZ0QIdpEeyAx/EP8blOBredM9xWfEXgiHVA==", + "optional": true, + "requires": { + "@aws-sdk/credential-provider-env": "3.292.0", + "@aws-sdk/credential-provider-imds": "3.292.0", + "@aws-sdk/credential-provider-ini": "3.294.0", + "@aws-sdk/credential-provider-process": "3.292.0", + "@aws-sdk/credential-provider-sso": "3.294.0", + "@aws-sdk/credential-provider-web-identity": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.292.0.tgz", + "integrity": "sha512-CFVXuMuUvg/a4tknzRikEDwZBnKlHs1LZCpTXIGjBdUTdosoi4WNzDLzGp93ZRTtcgFz+4wirz2f7P3lC0NrQw==", + "optional": true, + "requires": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.294.0.tgz", + "integrity": "sha512-UxrcAA/0l7j9+3tolYcG5M61D/IE1Bjd/9H87H1i2A2BrwUUBhW1Dp/vvROEDrrywlMDG3CDF3T/7ADtTak+sg==", + "optional": true, + "requires": { + "@aws-sdk/client-sso": "3.294.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/token-providers": "3.294.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.292.0.tgz", + "integrity": "sha512-4DbtIEM9gGVfqYlMdYXg3XY+vBhemjB1zXIequottW8loLYM8Vuz4/uGxxKNze6evVVzowsA0wKrYclE1aj/Rg==", + "optional": true, + "requires": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/credential-providers": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.294.0.tgz", + "integrity": "sha512-CRGwCs6F31mQzjYi5YF2Z6c1T+UrFKtJSa6Ff/Q1rrPnROexyhBVnpP8WzpkODx/pZxKtTX50IX7ehIxbFDIyQ==", + "optional": true, + "requires": { + "@aws-sdk/client-cognito-identity": "3.294.0", + "@aws-sdk/client-sso": "3.294.0", + "@aws-sdk/client-sts": "3.294.0", + "@aws-sdk/credential-provider-cognito-identity": "3.294.0", + "@aws-sdk/credential-provider-env": "3.292.0", + "@aws-sdk/credential-provider-imds": "3.292.0", + "@aws-sdk/credential-provider-ini": "3.294.0", + "@aws-sdk/credential-provider-node": "3.294.0", + "@aws-sdk/credential-provider-process": "3.292.0", + "@aws-sdk/credential-provider-sso": "3.294.0", + "@aws-sdk/credential-provider-web-identity": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/fetch-http-handler": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.292.0.tgz", + "integrity": "sha512-zh3bhUJbL8RSa39ZKDcy+AghtUkIP8LwcNlwRIoxMQh3Row4D1s4fCq0KZCx98NJBEXoiTLyTQlZxxI//BOb1Q==", + "optional": true, + "requires": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/querystring-builder": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-base64": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/hash-node": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.292.0.tgz", + "integrity": "sha512-1yLxmIsvE+eK36JXEgEIouTITdykQLVhsA5Oai//Lar6Ddgu1sFpLDbdkMtKbrh4I0jLN9RacNCkeVQjZPTCCQ==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-buffer-from": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/invalid-dependency": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.292.0.tgz", + "integrity": "sha512-39OUV78CD3TmEbjhpt+V+Fk4wAGWhixqHxDSN8+4WL0uB4Fl7k5m3Z9hNY78AttHQSl2twR7WtLztnXPAFsriw==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/is-array-buffer": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.292.0.tgz", + "integrity": "sha512-kW/G5T/fzI0sJH5foZG6XJiNCevXqKLxV50qIT4B1pMuw7regd4ALIy0HwSqj1nnn9mSbRWBfmby0jWCJsMcwg==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-content-length": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.292.0.tgz", + "integrity": "sha512-2gMWzQus5mj14menolpPDbYBeaOYcj7KNFZOjTjjI3iQ0KqyetG6XasirNrcJ/8QX1BRmpTol8Xjp2Ue3Gbzwg==", + "optional": true, + "requires": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-endpoint": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.292.0.tgz", + "integrity": "sha512-cPMkiSxpZGG6tYlW4OS+ucS6r43f9ddX9kcUoemJCY10MOuogdPjulCAjE0HTs2PLKSOrrG4CTP4Q4wWDrH4Bw==", + "optional": true, + "requires": { + "@aws-sdk/middleware-serde": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/signature-v4": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/url-parser": "3.292.0", + "@aws-sdk/util-config-provider": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.292.0.tgz", + "integrity": "sha512-mHuCWe3Yg2S5YZ7mB7sKU6C97XspfqrimWjMW9pfV2usAvLA3R0HrB03jpR5vpZ3P4q7HB6wK3S6CjYMGGRNag==", + "optional": true, + "requires": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.292.0.tgz", + "integrity": "sha512-yZNY1XYmG3NG+uonET7jzKXNiwu61xm/ZZ6i/l51SusuaYN+qQtTAhOFsieQqTehF9kP4FzbsWgPDwD8ZZX9lw==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.292.0.tgz", + "integrity": "sha512-kA3VZpPko0Zqd7CYPTKAxhjEv0HJqFu2054L04dde1JLr43ro+2MTdX7vsHzeAFUVRphqatFFofCumvXmU6Mig==", + "optional": true, + "requires": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-retry": { + "version": "3.293.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.293.0.tgz", + "integrity": "sha512-7tiaz2GzRecNHaZ6YnF+Nrtk3au8qF6oiipf11R7MJiqJ0fkMLnz/iRrlakDziS9qF/a9v+3yxb4W4NHK3f4Tw==", + "optional": true, + "requires": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/service-error-classification": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "@aws-sdk/util-retry": "3.292.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-sdk-sts": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.292.0.tgz", + "integrity": "sha512-GN5ZHEqXZqDi+HkVbaXRX9HaW/vA5rikYpWKYsmxTUZ7fB7ijvEO3co3lleJv2C+iGYRtUIHC4wYNB5xgoTCxg==", + "optional": true, + "requires": { + "@aws-sdk/middleware-signing": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/signature-v4": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-serde": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.292.0.tgz", + "integrity": "sha512-6hN9mTQwSvV8EcGvtXbS/MpK7WMCokUku5Wu7X24UwCNMVkoRHLIkYcxHcvBTwttuOU0d8hph1/lIX4dkLwkQw==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-signing": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.292.0.tgz", + "integrity": "sha512-GVfoSjDjEQ4TaO6x9MffyP3uRV+2KcS5FtexLCYOM9pJcnE9tqq9FJOrZ1xl1g+YjUVKxo4x8lu3tpEtIb17qg==", + "optional": true, + "requires": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/signature-v4": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-stack": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.292.0.tgz", + "integrity": "sha512-WdQpRkuMysrEwrkByCM1qCn2PPpFGGQ2iXqaFha5RzCdZDlxJni9cVNb6HzWUcgjLEYVTXCmOR9Wxm3CNW44Qg==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.293.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.293.0.tgz", + "integrity": "sha512-gZ7/e6XwpKk9mvgA78q4Ffc796jTn02TUKx2qMDnkLVbeJXBNN2jnvYEKq8v70+o7fd/ALRudg8gBDmkkhM/Hw==", + "optional": true, + "requires": { + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-endpoints": "3.293.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/node-config-provider": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.292.0.tgz", + "integrity": "sha512-S3NnC9dQ5GIbJYSDIldZb4zdpCOEua1tM7bjYL3VS5uqCEM93kIi/o/UkIUveMp/eqTS2LJa5HjNIz5Te6je0A==", + "optional": true, + "requires": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/node-http-handler": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.292.0.tgz", + "integrity": "sha512-L/E3UDSwXLXjt1XWWh0RBD55F+aZI1AEdPwdES9i1PjnZLyuxuDhEDptVibNN56+I9/4Q3SbmuVRVlOD0uzBag==", + "optional": true, + "requires": { + "@aws-sdk/abort-controller": "3.292.0", + "@aws-sdk/protocol-http": "3.292.0", + "@aws-sdk/querystring-builder": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/property-provider": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.292.0.tgz", + "integrity": "sha512-dHArSvsiqhno/g55N815gXmAMrmN8DP7OeFNqJ4wJG42xsF2PFN3DAsjIuHuXMwu+7A3R1LHqIpvv0hA9KeoJQ==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/protocol-http": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.292.0.tgz", + "integrity": "sha512-NLi4fq3k41aXIh1I97yX0JTy+3p6aW1NdwFwdMa674z86QNfb4SfRQRZBQe9wEnAZ/eWHVnlKIuII+U1URk/Kg==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/querystring-builder": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.292.0.tgz", + "integrity": "sha512-XElIFJaReIm24eEvBtV2dOtZvcm3gXsGu/ftG8MLJKbKXFKpAP1q+K6En0Bs7/T88voKghKdKpKT+eZUWgTqlg==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-uri-escape": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/querystring-parser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.292.0.tgz", + "integrity": "sha512-iTYpYo7a8X9RxiPbjjewIpm6XQPx2EOcF1dWCPRII9EFlmZ4bwnX+PDI36fIo9oVs8TIKXmwNGODU9nsg7CSAw==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/service-error-classification": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.292.0.tgz", + "integrity": "sha512-X1k3sixCeC45XSNHBe+kRBQBwPDyTFtFITb8O5Qw4dS9XWGhrUJT4CX0qE5aj8qP3F9U5nRizs9c2mBVVP0Caw==", + "optional": true + }, + "@aws-sdk/shared-ini-file-loader": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.292.0.tgz", + "integrity": "sha512-Av2TTYg1Jig2kbkD56ybiqZJB6vVrYjv1W5UQwY/q3nA/T2mcrgQ20ByCOt5Bv9VvY7FSgC+znj+L4a7RLGmBg==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/signature-v4": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.292.0.tgz", + "integrity": "sha512-+rw47VY5mvBecn13tDQTl1ipGWg5tE63faWgmZe68HoBL87ZiDzsd7bUKOvjfW21iMgWlwAppkaNNQayYRb2zg==", + "optional": true, + "requires": { + "@aws-sdk/is-array-buffer": "3.292.0", + "@aws-sdk/types": "3.292.0", + "@aws-sdk/util-hex-encoding": "3.292.0", + "@aws-sdk/util-middleware": "3.292.0", + "@aws-sdk/util-uri-escape": "3.292.0", + "@aws-sdk/util-utf8": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/smithy-client": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.292.0.tgz", + "integrity": "sha512-S8PKzjPkZ6SXYZuZiU787dMsvQ0d/LFEhw2OI4Oe2An9Fc2IwJ2FYukyHoQJOV2tV0DiuMebPo7eMyQyjKElvA==", + "optional": true, + "requires": { + "@aws-sdk/middleware-stack": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/token-providers": { + "version": "3.294.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.294.0.tgz", + "integrity": "sha512-6nwO04LtC5f4AsUvGZXyjaswuEK4Rr2VsuANpMKrPCgunRfI58a8YXLniudOSXN6e7CFJ6M3uo/h5YXqtnzGug==", + "optional": true, + "requires": { + "@aws-sdk/client-sso-oidc": "3.294.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/shared-ini-file-loader": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/types": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.292.0.tgz", + "integrity": "sha512-1teYAY2M73UXZxMAxqZxVS2qwXjQh0OWtt7qyLfha0TtIk/fZ1hRwFgxbDCHUFcdNBSOSbKH/ESor90KROXLCQ==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/url-parser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.292.0.tgz", + "integrity": "sha512-NZeAuZCk1x6TIiWuRfbOU6wHPBhf0ly2qOHzWut4BCH+b4RrDmFF8EmXcH1auEfGhE7yRyR6XqIN0t3S+hYACA==", + "optional": true, + "requires": { + "@aws-sdk/querystring-parser": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-base64": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.292.0.tgz", + "integrity": "sha512-zjNCwNdy617yFvEjZorepNWXB2sQCVfsShCwFy/kIQ5iW5tT2jQKaqc0K77diU9atkooxw9p1W9m9sOgrkOFNw==", + "optional": true, + "requires": { + "@aws-sdk/util-buffer-from": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-body-length-browser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.292.0.tgz", + "integrity": "sha512-Wd/BM+JsMiKvKs/bN3z6TredVEHh2pKudGfg3CSjTRpqFpOG903KDfyHBD42yg5PuCHoHoewJvTPKwgn7/vhaw==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-body-length-node": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.292.0.tgz", + "integrity": "sha512-BBgipZ2P6RhogWE/qj0oqpdlyd3iSBYmb+aD/TBXwB2lA/X8A99GxweBd/kp06AmcJRoMS9WIXgbWkiiBlRlSA==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-buffer-from": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.292.0.tgz", + "integrity": "sha512-RxNZjLoXNxHconH9TYsk5RaEBjSgTtozHeyIdacaHPj5vlQKi4hgL2hIfKeeNiAfQEVjaUFF29lv81xpNMzVMQ==", + "optional": true, + "requires": { + "@aws-sdk/is-array-buffer": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-config-provider": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.292.0.tgz", + "integrity": "sha512-t3noYll6bPRSxeeNNEkC5czVjAiTPcsq00OwfJ2xyUqmquhLEfLwoJKmrT1uP7DjIEXdUtfoIQ2jWiIVm/oO5A==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-defaults-mode-browser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.292.0.tgz", + "integrity": "sha512-7+zVUlMGfa8/KT++9humHo6IDxTnxMCmWUj5jVNlkpk6h7Ecmppf7aXotviyVIA43lhtz0p2AErs0N0ekEUK+w==", + "optional": true, + "requires": { + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-defaults-mode-node": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.292.0.tgz", + "integrity": "sha512-SSIw85eF4BVs0fOJRyshT+R3b/UmBPhiVKCUZm2rq6+lIGkDPiSwQU3d/80AhXtiL5SFT/IzAKKgQd8qMa7q3A==", + "optional": true, + "requires": { + "@aws-sdk/config-resolver": "3.292.0", + "@aws-sdk/credential-provider-imds": "3.292.0", + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/property-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-endpoints": { + "version": "3.293.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.293.0.tgz", + "integrity": "sha512-R/99aNV49Refpv5guiUjEUrZYlvnfaNBniB+/ZtMO3ixxUopapssCrUivuJrmhccmrYaTCZw7dRzIWjU1jJhKg==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-hex-encoding": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.292.0.tgz", + "integrity": "sha512-qBd5KFIUywQ3qSSbj814S2srk0vfv8A6QMI+Obs1y2LHZFdQN5zViptI4UhXhKOHe+NnrHWxSuLC/LMH6q3SmA==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-locate-window": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.292.0.tgz", + "integrity": "sha512-6xnFJXZI9pKw5lQCDvuWA5PnOaUtNRKWwdxvGkkLx5orboFaoVMS6zowjSQxwVNRjW82u6dYNkhmj9mZ8VSjWg==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-middleware": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.292.0.tgz", + "integrity": "sha512-KjhS7flfoBKDxbiBZjLjMvEizXgjfQb7GQEItgzGoI9rfGCmZtvqCcqQQoIlxb8bIzGRggAUHtBGWnlLbpb+GQ==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-retry": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.292.0.tgz", + "integrity": "sha512-JEHyF7MpVeRF5uR4LDYgpOKcFpOPiAj8TqN46SVOQQcL1K+V7cSr7O7N7J6MwJaN9XOzAcBadeIupMm7/BFbgw==", + "optional": true, + "requires": { + "@aws-sdk/service-error-classification": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-uri-escape": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.292.0.tgz", + "integrity": "sha512-hOQtUMQ4VcQ9iwKz50AoCp1XBD5gJ9nly/gJZccAM7zSA5mOO8RRKkbdonqquVHxrO0CnYgiFeCh3V35GFecUw==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.292.0.tgz", + "integrity": "sha512-dld+lpC3QdmTQHdBWJ0WFDkXDSrJgfz03q6mQ8+7H+BC12ZhT0I0g9iuvUjolqy7QR00OxOy47Y9FVhq8EC0Gg==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.292.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.292.0.tgz", + "integrity": "sha512-f+NfIMal5E61MDc5WGhUEoicr7b1eNNhA+GgVdSB/Hg5fYhEZvFK9RZizH5rrtsLjjgcr9nPYSR7/nDKCJLumw==", + "optional": true, + "requires": { + "@aws-sdk/node-config-provider": "3.292.0", + "@aws-sdk/types": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-utf8": { + "version": "3.292.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.292.0.tgz", + "integrity": "sha512-FPkj+Z59/DQWvoVu2wFaRncc3KVwe/pgK3MfVb0Lx+Ibey5KUx+sNpJmYcVYHUAe/Nv/JeIpOtYuC96IXOnI6w==", + "optional": true, + "requires": { + "@aws-sdk/util-buffer-from": "3.292.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, + "@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + } + } + }, "@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true }, + "@eslint/eslintrc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", + "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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 + } + } + }, + "@eslint/js": { + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", + "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", + "dev": true + }, "@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -4187,6 +8523,52 @@ "@hapi/hoek": "^9.0.0" } }, + "@hcaptcha/types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@hcaptcha/types/-/types-1.0.3.tgz", + "integrity": "sha512-1mbU6eSGawRrqeahRrOzZo/SVLI6oZ5/azuBpSyVrRRR96CnS3fOVDWfzxpngfxKD0/I9Rwu6c/3ITqD8rXeTQ==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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 + } + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@mapbox/node-pre-gyp": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", @@ -4203,6 +8585,32 @@ "tar": "^6.1.11" } }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@oozcitak/dom": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-0.0.11.tgz", @@ -4300,9 +8708,9 @@ "requires": {} }, "@redis/client": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.5.tgz", - "integrity": "sha512-fuMnpDYSjT5JXR9rrCW1YWA4L8N/9/uS4ImT3ZEC/hcaQRI1D/9FvwjriRj1UvepIgzZXthFVKMNRzP/LNL7BQ==", + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.6.tgz", + "integrity": "sha512-dFD1S6je+A47Lj22jN/upVU2fj4huR7S9APd7/ziUXsIXDL+11GPYti4Suv5y8FuXaN+0ZG4JF+y1houEJ7ToA==", "requires": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -4322,9 +8730,9 @@ "requires": {} }, "@redis/search": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.1.tgz", - "integrity": "sha512-pqCXTc5e7wJJgUuJiC3hBgfoFRoPxYzwn0BEfKgejTM7M/9zP3IpUcqcjgfp8hF+LoV8rHZzcNTz7V+pEIY7LQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz", + "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==", "requires": {} }, "@redis/time-series": { @@ -4364,14 +8772,25 @@ "defer-to-connect": "^2.0.0" } }, - "@types/bson": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz", - "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==", + "@types/bcrypt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "dev": true, "requires": { "@types/node": "*" } }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, "@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -4383,11 +8802,86 @@ "@types/responselike": "^1.0.0" } }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/dicer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@types/dicer/-/dicer-0.2.2.tgz", + "integrity": "sha512-UPLqCYey+jn5Mf57KFDwxD/7VZYDsbYUi3iyTehLFVjlbvl/JcUTPaot8uKNYLO0EoZpey+rC/s5AF3VxfeC2Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/fs-extra": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.1.tgz", + "integrity": "sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==", + "dev": true, + "requires": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, "@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/jsonfile": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.1.tgz", + "integrity": "sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -4396,19 +8890,151 @@ "@types/node": "*" } }, - "@types/mongodb": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", - "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "@types/mongoose-unique-validator": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/mongoose-unique-validator/-/mongoose-unique-validator-1.0.7.tgz", + "integrity": "sha512-wgPWN7x9mHdy8x1jcluDF7EDWFb/s8LD7skLr012C8i2Q3tUhdTlp3JxRhFbEM3TD7J1INaoTrWaSaguhuqilQ==", + "dev": true, + "requires": { + "mongoose": "^6.3.0" + }, + "dependencies": { + "bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "dev": true, + "requires": { + "buffer": "^5.6.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "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 + } + } + }, + "mongodb": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.14.0.tgz", + "integrity": "sha512-coGKkWXIBczZPr284tYKFLg+KbGPPLlSbdgfKAb6QqCFt5bo5VFZ50O3FFzsw4rnkqjwT6D8Qcoo9nshYKM7Mg==", + "dev": true, + "requires": { + "@aws-sdk/credential-providers": "^3.186.0", + "bson": "^4.7.0", + "mongodb-connection-string-url": "^2.5.4", + "saslprep": "^1.0.3", + "socks": "^2.7.1" + } + }, + "mongoose": { + "version": "6.10.5", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.10.5.tgz", + "integrity": "sha512-y4HL4/9EySec7L0gJ+pCm9heLSF45uIIvRS4fSeAFWDfe4vXW1vRZJwTz7OGkra3ZoSfRnFTo9bNZkuggDVlVA==", + "dev": true, + "requires": { + "bson": "^4.7.0", + "kareem": "2.5.1", + "mongodb": "4.14.0", + "mpath": "0.9.0", + "mquery": "4.0.3", + "ms": "2.1.3", + "sift": "16.0.1" + } + }, + "mquery": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", + "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", + "dev": true, + "requires": { + "debug": "4.x" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "@types/morgan": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz", + "integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==", + "dev": true, "requires": { - "@types/bson": "*", "@types/node": "*" } }, + "@types/ndarray": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.11.tgz", + "integrity": "sha512-hOZVTN24zDHwCHaW7mF9n1vHJt83fZhNZ0YYRBwQGhA96yBWWDPTDDlqJatagHIOJB0a4xoNkNc+t/Cxd+6qUA==", + "dev": true + }, "@types/node": { - "version": "18.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", - "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" + "version": "18.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.6.tgz", + "integrity": "sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==" + }, + "@types/node-rsa": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/node-rsa/-/node-rsa-1.1.1.tgz", + "integrity": "sha512-itzxtaBgk4OMbrCawVCvas934waMZWjW17v7EYgFVlfYS/cl0/P7KZdojWCq9SDJMI5cnLQLUP8ayhVCTY8TEg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/nodemailer": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.7.tgz", + "integrity": "sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true }, "@types/responselike": { "version": "1.0.0", @@ -4418,6 +9044,227 @@ "@types/node": "*" } }, + "@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "@types/serve-static": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/validator": { + "version": "13.7.14", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.14.tgz", + "integrity": "sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g==", + "dev": true + }, + "@types/w3c-css-typed-object-model-level-1": { + "version": "20180410.0.5", + "resolved": "https://registry.npmjs.org/@types/w3c-css-typed-object-model-level-1/-/w3c-css-typed-object-model-level-1-20180410.0.5.tgz", + "integrity": "sha512-khoqDQqA/fUdK6mEJtQEfMAPCKkk8E1D+ahM/0HCy3mc5L33wJinqQNXu9Uf4+mqzMvORljHg76jvrmwS0bEjA==", + "dev": true + }, + "@types/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" + }, + "@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "requires": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz", + "integrity": "sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/type-utils": "5.54.1", + "@typescript-eslint/utils": "5.54.1", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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/parser": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz", + "integrity": "sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/typescript-estree": "5.54.1", + "debug": "^4.3.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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/scope-manager": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz", + "integrity": "sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/visitor-keys": "5.54.1" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz", + "integrity": "sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.54.1", + "@typescript-eslint/utils": "5.54.1", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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/types": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.1.tgz", + "integrity": "sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz", + "integrity": "sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/visitor-keys": "5.54.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "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/utils": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.1.tgz", + "integrity": "sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.54.1", + "@typescript-eslint/types": "5.54.1", + "@typescript-eslint/typescript-estree": "5.54.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz", + "integrity": "sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.54.1", + "eslint-visitor-keys": "^3.3.0" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -4437,6 +9284,13 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, "acorn-node": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", @@ -4491,10 +9345,14 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } }, "aproba": { "version": "2.0.0", @@ -4511,9 +9369,9 @@ }, "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==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -4530,6 +9388,12 @@ } } }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", @@ -4545,6 +9409,12 @@ "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==" }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, "arraybuffer-to-string": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/arraybuffer-to-string/-/arraybuffer-to-string-1.0.2.tgz", @@ -4574,11 +9444,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, "atob-lite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", @@ -4590,9 +9455,9 @@ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" }, "aws-sdk": { - "version": "2.1313.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1313.0.tgz", - "integrity": "sha512-8GMdtV2Uch3HL2c6+P3lNZFTcg/fqq9L3EWYRLb6ljCZvWKTssjdkjSJFDyTReNgeiKV224YRPYQbKpOEz4flQ==", + "version": "2.1328.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1328.0.tgz", + "integrity": "sha512-ud8ieE+hGX/cWHkQ9kMxQw6w+onbv71PDrcPmP2j8cmYv5IPlM5Zh8/tpsmXApLYDmQMuZ3TtssiB1KmoSbzgA==", "requires": { "buffer": "4.9.2", "events": "1.1.1", @@ -4663,49 +9528,6 @@ "resolved": "https://registry.npmjs.org/bit-buffer/-/bit-buffer-0.2.5.tgz", "integrity": "sha512-x1yGnmXvFg6e3DiyRztElbcn1bsCTFSoM/ncAzY62uE0JdTl5xlKJd0ooqLYoPbhdsnpehSIQrdIvclcZJYwiA==" }, - "bl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", - "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - }, - "dependencies": { - "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" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "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" - } - } - } - }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, "bmp-js": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", @@ -4730,6 +9552,12 @@ "unpipe": "1.0.0" } }, + "bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "optional": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -4739,6 +9567,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "brfs": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brfs/-/brfs-2.0.2.tgz", @@ -4751,9 +9588,9 @@ } }, "bson": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", - "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-5.0.1.tgz", + "integrity": "sha512-y09gBGusgHtinMon/GVbv1J6FrXhnr/+6hqLlSmEFzkz6PodqF6TxjyvfvY3AfO+oG1mgUtbC86xSbOlwvM62Q==" }, "buffer": { "version": "4.9.2", @@ -4785,15 +9622,6 @@ "resolved": "https://registry.npmjs.org/buffer-to-uint8array/-/buffer-to-uint8array-1.1.0.tgz", "integrity": "sha512-JVTSbtA6YuOGdu5NL0ffizsBwuwbTXfV7OC91FhazMz9UKP/KlDS+Z7wuiSRClbnTQz52fJgVXI9YDXQRVl2sQ==" }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==", - "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - } - }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -4827,11 +9655,27 @@ "get-intrinsic": "^1.0.2" } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -4855,6 +9699,21 @@ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -4890,9 +9749,9 @@ }, "dependencies": { "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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -4918,65 +9777,6 @@ } } }, - "connect-multiparty": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/connect-multiparty/-/connect-multiparty-2.2.0.tgz", - "integrity": "sha512-zKcpA7cuXGEhuw9Pz7JmVCFmp85jzGLGm/iiagXTwyEAJp4ypLPtRS/V4IGuGb9KjjrgHBs6P/gDCpZHnFzksA==", - "requires": { - "http-errors": "~1.7.0", - "multiparty": "~4.2.1", - "on-finished": "~2.3.0", - "qs": "~6.5.2", - "type-is": "~1.6.16" - }, - "dependencies": { - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" - }, - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "requires": { - "ee-first": "1.1.1" - } - }, - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - } - } - }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -5024,6 +9824,17 @@ "vary": "^1" } }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", @@ -5095,11 +9906,6 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, - "denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" - }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -5124,6 +9930,24 @@ "streamsearch": "0.1.2" } }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -5143,9 +9967,9 @@ }, "dependencies": { "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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -5286,6 +10110,12 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, "escodegen": { "version": "1.14.3", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", @@ -5298,11 +10128,209 @@ "source-map": "~0.6.1" } }, + "eslint": { + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", + "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^2.0.0", + "@eslint/js": "8.35.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.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 + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + } + } + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + } + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", @@ -5375,47 +10403,11 @@ "vary": "~1.1.2" } }, - "express-form-data": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/express-form-data/-/express-form-data-2.0.19.tgz", - "integrity": "sha512-QWulRIYvZV/ZOuO0+SNpCs9Gl0st9SssYtm5Icfm17p2MU4HASLpd4K5w8fwnXB/lPPskecVSeqjVWi2SwIb1A==", - "requires": { - "connect-multiparty": "^2.2.0", - "fs-extra": "^9.1.0", - "signal-exit": "^3.0.7" - }, - "dependencies": { - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - } - } - }, "express-rate-limit": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.5.1.tgz", - "integrity": "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg==" + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz", + "integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==", + "requires": {} }, "express-subdomain": { "version": "1.0.5", @@ -5458,6 +10450,30 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -5468,11 +10484,47 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "fast-xml-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", + "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==", + "optional": true, + "requires": { + "strnum": "^1.0.5" + } + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "file-type": { "version": "10.11.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==" }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -5487,6 +10539,32 @@ "unpipe": "~1.0.0" } }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, "flatten-vertex-data": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz", @@ -5523,11 +10601,6 @@ "mime-types": "^2.1.12" } }, - "formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==" - }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -5641,6 +10714,38 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -5672,6 +10777,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -5694,6 +10805,12 @@ "function-bind": "^1.1.1" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -5790,6 +10907,12 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, "image-pixels": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/image-pixels/-/image-pixels-1.1.1.tgz", @@ -5831,6 +10954,22 @@ "file-type": "^10.9.0" } }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5845,6 +10984,17 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==", + "dev": true + }, + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -5892,6 +11042,12 @@ "has": "^1.0.3" } }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "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", @@ -5905,6 +11061,27 @@ "has-tostringtag": "^1.0.0" } }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -5942,6 +11119,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -5953,9 +11136,9 @@ "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" }, "joi": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.7.1.tgz", - "integrity": "sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA==", + "version": "17.8.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.8.3.tgz", + "integrity": "sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w==", "requires": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", @@ -5969,6 +11152,21 @@ "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz", "integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==" }, + "js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -5989,6 +11187,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -6013,15 +11217,10 @@ "verror": "1.10.0" } }, - "kaitai-struct": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/kaitai-struct/-/kaitai-struct-0.9.0.tgz", - "integrity": "sha512-mfoBu9+IGqaY3ykG1TyAy9omOAZWtheqESQOvo/HKIQVTz+gRPVCNBnhjbO+8wAQ77RD33wYvLBWmITuXIviQg==" - }, "kareem": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", - "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==" }, "keyv": { "version": "4.5.2", @@ -6040,6 +11239,15 @@ "type-check": "~0.3.2" } }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -6056,10 +11264,10 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==" + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "lowercase-keys": { "version": "2.0.0", @@ -6128,11 +11336,27 @@ } } }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, "mii-js": { "version": "git+ssh://git@github.com/PretendoNetwork/mii-js.git#5d8eb8013514a13b0df6eb4a5bfd8b5a63fb9861", "from": "mii-js@github:PretendoNetwork/mii-js", @@ -6177,9 +11401,9 @@ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "minipass": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.3.tgz", - "integrity": "sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw==" + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz", + "integrity": "sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==" }, "minizlib": { "version": "2.1.2", @@ -6201,90 +11425,63 @@ } }, "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { - "minimist": "^1.2.6" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, - "moment-timezone": { - "version": "0.5.40", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz", - "integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==", + "mongodb": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.1.0.tgz", + "integrity": "sha512-qgKb7y+EI90y4weY3z5+lIgm8wmexbonz0GalHkSElQXVKtRuwqXuhXKccyvIjXCJVy9qPV82zsinY0W1FBnJw==", "requires": { - "moment": ">= 2.9.0" + "bson": "^5.0.1", + "mongodb-connection-string-url": "^2.6.0", + "saslprep": "^1.0.3", + "socks": "^2.7.1" } }, - "mongodb": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz", - "integrity": "sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==", + "mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", "requires": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "optional-require": "^1.1.8", - "safe-buffer": "^5.1.2", - "saslprep": "^1.0.0" - }, - "dependencies": { - "optional-require": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", - "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", - "requires": { - "require-at": "^1.0.6" - } - } + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" } }, "mongoose": { - "version": "5.13.15", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.15.tgz", - "integrity": "sha512-cxp1Gbb8yUWkaEbajdhspSaKzAvsIvOtRlYD87GN/P2QEUhpd6bIvebi36T6M0tIVAMauNaK9SPA055N3PwF8Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.0.0.tgz", + "integrity": "sha512-U0YPURDld+k/nvvSG1mRClQSjZMRXwQKSU5yb9PslRnOmVz0UlBD7SjSnjUuGT0yk+7BH+kJNimsKqMxYAKkMA==", "requires": { - "@types/bson": "1.x || 4.0.x", - "@types/mongodb": "^3.5.27", - "bson": "^1.1.4", - "kareem": "2.3.2", - "mongodb": "3.7.3", - "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.8.4", - "mquery": "3.2.5", - "ms": "2.1.2", - "optional-require": "1.0.x", - "regexp-clone": "1.0.0", - "safe-buffer": "5.2.1", - "sift": "13.5.2", - "sliced": "1.0.1" + "bson": "^5.0.1", + "kareem": "2.5.1", + "mongodb": "5.1.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "16.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==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, - "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==", - "requires": {} - }, "mongoose-unique-validator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mongoose-unique-validator/-/mongoose-unique-validator-2.0.3.tgz", - "integrity": "sha512-3/8pmvAC1acBZS6eWKAWQUiZBlARE1wyWtjga4iQ2wDJeOfRlIKmAvTNHSZXKaAf7RCRUd7wh7as6yWAOrjpQg==", + "version": "git+ssh://git@github.com/stenneepro/mongoose-unique-validator.git#1cd976b70ddd8e8e65fed1d9a387677713a8d177", + "from": "mongoose-unique-validator@github:stenneepro/mongoose-unique-validator#bump/mongoose7", "requires": { "lodash.foreach": "^4.1.0", - "lodash.get": "^4.0.2" + "lodash.get": "^4.0.2", + "lodash.merge": "^4.6.2" } }, "morgan": { @@ -6310,34 +11507,30 @@ } }, "mpath": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", - "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" }, "mquery": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", - "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", "requires": { - "bluebird": "3.5.1", - "debug": "3.1.0", - "regexp-clone": "^1.0.0", - "safe-buffer": "5.1.2", - "sliced": "1.0.1" + "debug": "4.x" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -6346,66 +11539,52 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "multer": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", - "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", - "requires": { - "append-field": "^1.0.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - } - }, - "multiparty": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.3.tgz", - "integrity": "sha512-Ak6EUJZuhGS8hJ3c2fY6UW5MbkGUPMBEGd13djUzoY/BHqV/gTuFWtC6IuVA7A2+v3yjBS6c4or50xhzTQZImQ==", - "requires": { - "http-errors": "~1.8.1", - "safe-buffer": "5.2.1", - "uid-safe": "2.1.5" - }, - "dependencies": { - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" - }, - "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" - } - } - }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "dev": true, + "requires": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + } + } + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "nested-error-stacks": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", + "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==" + }, "next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -6422,13 +11601,33 @@ "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "requires": { "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } } }, "node-rsa": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.1.1.tgz", "integrity": "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==", - "dev": true, "requires": { "asn1": "^0.2.4" } @@ -6503,11 +11702,6 @@ "wrappy": "1" } }, - "optional-require": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", - "integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA==" - }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -6526,6 +11720,33 @@ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "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" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-rect": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/parse-rect/-/parse-rect-1.2.0.tgz", @@ -6539,11 +11760,23 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -6554,6 +11787,12 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -6564,6 +11803,12 @@ "resolved": "https://registry.npmjs.org/pick-by-alias/-/pick-by-alias-1.2.0.tgz", "integrity": "sha512-ESj2+eBxhGrcA1azgHs7lARG5+5iLakc/6nlfbpjcLl00HuuUOIuORhYXN4D1HfvMSKuVtFQjAlnwi1JHEeDIw==" }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, "pngjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", @@ -6638,6 +11883,12 @@ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" }, + "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", @@ -6653,11 +11904,6 @@ "through2": "^2.0.0" } }, - "random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==" - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -6702,22 +11948,29 @@ } }, "redis": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.4.tgz", - "integrity": "sha512-wi2tgDdQ+Q8q+PR5FLRx4QvDiWaA+PoJbrzsyFqlClN5R4LplHqN3scs/aGjE//mbz++W19SgxiEnQ27jnCRaA==", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.5.tgz", + "integrity": "sha512-O0OWA36gDQbswOdUuAhRL6mTZpHFN525HlgZgDaVNgCJIAZR3ya06NTESb0R+TUZ+BFaDpz6NnnVvoMx9meUFg==", "requires": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.5", + "@redis/client": "1.5.6", "@redis/graph": "1.1.0", "@redis/json": "1.0.4", - "@redis/search": "1.1.1", + "@redis/search": "1.1.2", "@redis/time-series": "1.0.4" } }, - "regexp-clone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", - "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "optional": true + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true }, "request": { "version": "2.88.2", @@ -6758,11 +12011,6 @@ } } }, - "require-at": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", - "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==" - }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -6778,6 +12026,12 @@ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" }, + "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 + }, "responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", @@ -6791,6 +12045,12 @@ "resolved": "https://registry.npmjs.org/restructure/-/restructure-2.0.1.tgz", "integrity": "sha512-e0dOpjm5DseomnXx2M5lpdZ5zoHqF1+bqdMJUohoYVVQa7cBdnk7fdmeI6byNWP/kiME72EeTiSypTCVnpLiDg==" }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "revalidator": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", @@ -6805,6 +12065,15 @@ "glob": "^7.1.3" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -6904,6 +12173,21 @@ "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==" }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -6915,19 +12199,34 @@ } }, "sift": { - "version": "13.5.2", - "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", - "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==" + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" + }, + "socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + } }, "source-map": { "version": "0.6.1", @@ -7001,9 +12300,9 @@ }, "dependencies": { "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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -7078,6 +12377,27 @@ "ansi-regex": "^5.0.1" } }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -7094,15 +12414,14 @@ "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - } } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "tga": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/tga/-/tga-1.0.7.tgz", @@ -7122,9 +12441,9 @@ }, "dependencies": { "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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -7160,6 +12479,15 @@ "string-to-arraybuffer": "^1.0.0" } }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -7175,9 +12503,25 @@ } }, "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "requires": { + "punycode": "^2.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "requires": { + "tslib": "^1.8.1" + } }, "tunnel-agent": { "version": "0.6.0", @@ -7205,6 +12549,12 @@ "prelude-ls": "~1.1.2" } }, + "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 + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -7219,12 +12569,19 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, - "uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + }, + "typescript-is": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/typescript-is/-/typescript-is-0.19.0.tgz", + "integrity": "sha512-SAJEx2cxbQZhfOjDEjPnQJt1qRS1M3wrKbUwvsywVHWGbMgM1dcIf9gPWNDS1/dgTa/7Iexk2mmAHHsP9MeCsA==", "requires": { - "random-bytes": "~1.0.0" + "nested-error-stacks": "^2", + "reflect-metadata": ">=0.1.12", + "tsutils": "^3.17.1" } }, "universalify": { @@ -7316,17 +12673,26 @@ } }, "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" }, "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" } }, "which-typed-array": { @@ -7437,6 +12803,12 @@ "resolved": "https://registry.npmjs.org/yesno/-/yesno-0.4.0.tgz", "integrity": "sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==", "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index bed4955..5409fc7 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,15 @@ { "name": "account", - "version": "1.0.0", + "version": "2.0.0", "description": "", - "main": "./src/server.js", + "main": "./dist/server.js", "scripts": { - "lint": "./node_modules/.bin/eslint .", + "lint": "npx eslint .", + "build": "npm run lint && npm run clean && npx tsc && npx tsc-alias && npm run copy-static", + "clean": "rimraf ./dist", + "copy-static": "npm run copy-assets && npm run copy-timezones", + "copy-assets": "cp -r ./src/assets ./dist/assets", + "copy-timezones": "cp ./src/services/nnid/timezones.json ./dist/services/nnid/timezones.json", "start": "node .", "start:dev": "NODE_ENV=development node ." }, @@ -28,35 +33,49 @@ "dotenv": "^16.0.3", "email-validator": "^2.0.4", "express": "^4.17.1", - "express-form-data": "^2.0.17", - "express-rate-limit": "^5.3.0", + "express-rate-limit": "^6.7.0", "express-subdomain": "^1.0.5", - "formidable": "^1.2.2", "fs-extra": "^8.1.0", "got": "^11.8.2", "hcaptcha": "^0.1.0", "image-pixels": "^1.1.1", - "joi": "^17.6.1", - "kaitai-struct": "^0.9.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", + "joi": "^17.8.3", "mii-js": "github:PretendoNetwork/mii-js", - "moment": "^2.24.0", - "moment-timezone": "^0.5.27", - "mongoose": "^5.8.3", - "mongoose-unique-validator": "^2.0.3", + "moment": "^2.29.4", + "mongoose": "^7.0.0", + "mongoose-unique-validator": "github:stenneepro/mongoose-unique-validator#bump/mongoose7", "morgan": "^1.9.1", - "multer": "^1.4.3", + "node-rsa": "^1.0.7", "nodemailer": "^6.4.2", "redis": "^4.3.1", "tga": "^1.0.4", + "typescript-is": "^0.19.0", "validator": "^13.7.0", "xmlbuilder": "^13.0.2", "xmlbuilder2": "0.0.4" }, "devDependencies": { - "node-rsa": "^1.0.7", + "@hcaptcha/types": "^1.0.3", + "@types/bcrypt": "^5.0.0", + "@types/cors": "^2.8.13", + "@types/dicer": "^0.2.2", + "@types/express": "^4.17.17", + "@types/fs-extra": "^11.0.1", + "@types/mongoose-unique-validator": "^1.0.7", + "@types/morgan": "^1.9.4", + "@types/ndarray": "^1.0.11", + "@types/node": "^18.14.4", + "@types/node-rsa": "^1.1.1", + "@types/nodemailer": "^6.4.7", + "@types/qs": "^6.9.7", + "@types/validator": "^13.7.14", + "@types/w3c-css-typed-object-model-level-1": "^20180410.0.5", + "@typescript-eslint/eslint-plugin": "^5.54.1", + "@typescript-eslint/parser": "^5.54.1", + "eslint": "^8.35.0", + "ndarray": "^1.0.19", "prompt": "^1.0.0", + "typescript": "^4.9.5", "yesno": "^0.4.0" } } diff --git a/src/cache.js b/src/cache.js deleted file mode 100644 index 6cd89e2..0000000 --- a/src/cache.js +++ /dev/null @@ -1,212 +0,0 @@ -const fs = require('fs-extra'); -const redis = require('redis'); -const { config, disabledFeatures } = require('./config-manager'); -let client; - -const memoryCache = {}; - -const SERVICE_CERTS_BASE = `${__dirname}/../certs/service`; -const NEX_CERTS_BASE = `${__dirname}/../certs/nex`; -const LOCAL_CDN_BASE = `${__dirname}/../cdn`; - -async function connect() { - if (!disabledFeatures.redis) { - client = redis.createClient(config.redis.client); - client.on('error', (err) => console.log('Redis Client Error', err)); - - await client.connect(); - } -} - -async function setCachedFile(fileName, value) { - if (disabledFeatures.redis) { - memoryCache[fileName] = value; - } else { - await client.set(fileName, value); - } -} - -async function getCachedFile(fileName, encoding) { - let cachedFile; - - if (disabledFeatures.redis) { - cachedFile = memoryCache[fileName] || null; - } else { - cachedFile = await client.get(fileName); - } - - if (cachedFile !== null) { - cachedFile = Buffer.from(cachedFile, encoding); - } - - return cachedFile; -} - -// NEX server cache functions - -async function getNEXPublicKey(name, encoding) { - let publicKey = await getCachedFile(`nex:${name}:public_key`, encoding); - - if (publicKey === null) { - publicKey = await fs.readFile(`${NEX_CERTS_BASE}/${name}/public.pem`, { encoding }); - await setNEXPublicKey(name, publicKey); - } - - return publicKey; -} - -async function getNEXPrivateKey(name, encoding) { - let privateKey = await getCachedFile(`nex:${name}:private_key`, encoding); - - if (privateKey === null) { - privateKey = await fs.readFile(`${NEX_CERTS_BASE}/${name}/private.pem`, { encoding }); - await setNEXPrivateKey(name, privateKey); - } - - return privateKey; -} - -async function getNEXSecretKey(name, encoding) { - let secretKey = await getCachedFile(`nex:${name}:secret_key`, encoding); - - if (secretKey === null) { - const fileBuffer = await fs.readFile(`${NEX_CERTS_BASE}/${name}/secret.key`, { encoding: 'utf8' }); - secretKey = Buffer.from(fileBuffer, encoding); - await setNEXSecretKey(name, secretKey); - } - - return secretKey; -} - -async function getNEXAESKey(name, encoding) { - let aesKey = await getCachedFile(`nex:${name}:aes_key`, encoding); - - if (aesKey === null) { - const fileBuffer = await fs.readFile(`${NEX_CERTS_BASE}/${name}/aes.key`, { encoding: 'utf8' }); - aesKey = Buffer.from(fileBuffer, encoding); - await setNEXAESKey(name, aesKey); - } - - return aesKey; -} - -async function setNEXPublicKey(name, value) { - await setCachedFile(`nex:${name}:public_key`, value); -} - -async function setNEXPrivateKey(name, value) { - await setCachedFile(`nex:${name}:private_key`, value); -} - -async function setNEXSecretKey(name, value) { - await setCachedFile(`nex:${name}:secret_key`, value); -} - -async function setNEXAESKey(name, value) { - await setCachedFile(`nex:${name}:aes_key`, value); -} - -// 3rd party service cache functions - -async function getServicePublicKey(name, encoding) { - let publicKey = await getCachedFile(`service:${name}:public_key`, encoding); - - if (publicKey === null) { - publicKey = await fs.readFile(`${SERVICE_CERTS_BASE}/${name}/public.pem`, { encoding }); - await setServicePublicKey(name, publicKey); - } - - return publicKey; -} - -async function getServicePrivateKey(name, encoding) { - let privateKey = await getCachedFile(`service:${name}:private_key`, encoding); - - if (privateKey === null) { - privateKey = await fs.readFile(`${SERVICE_CERTS_BASE}/${name}/private.pem`, { encoding }); - await setServicePrivateKey(name, privateKey); - } - - return privateKey; -} - -async function getServiceSecretKey(name, encoding) { - let secretKey = await getCachedFile(`service:${name}:secret_key`, encoding); - - if (secretKey === null) { - const fileBuffer = await fs.readFile(`${SERVICE_CERTS_BASE}/${name}/secret.key`, { encoding: 'utf8' }); - secretKey = Buffer.from(fileBuffer, encoding); - await setServiceSecretKey(name, secretKey); - } - - return secretKey; -} - -async function getServiceAESKey(name, encoding) { - let aesKey = await getCachedFile(`service:${name}:aes_key`, encoding); - - if (aesKey === null) { - const fileBuffer = await fs.readFile(`${SERVICE_CERTS_BASE}/${name}/aes.key`, { encoding: 'utf8' }); - aesKey = Buffer.from(fileBuffer, encoding); - await setServiceAESKey(name, aesKey); - } - - return aesKey; -} - -async function setServicePublicKey(name, value) { - await setCachedFile(`service:${name}:public_key`, value); -} - -async function setServicePrivateKey(name, value) { - await setCachedFile(`service:${name}:private_key`, value); -} - -async function setServiceSecretKey(name, value) { - await setCachedFile(`service:${name}:secret_key`, value); -} - -async function setServiceAESKey(name, value) { - await setCachedFile(`service:${name}:aes_key`, value); -} - -// Local CDN cache functions - -async function getLocalCDNFile(name, encoding) { - let file = await getCachedFile(`local_cdn:${name}`, encoding); - - if (file === null) { - if (await fs.pathExists(`${LOCAL_CDN_BASE}/${name}`)) { - file = await fs.readFile(`${LOCAL_CDN_BASE}/${name}`, { encoding }); - await setLocalCDNFile(name, file); - } - } - - return file; -} - -async function setLocalCDNFile(name, value) { - await setCachedFile(`local_cdn:${name}`, value); -} - -module.exports = { - connect, - getNEXPublicKey, - getNEXPrivateKey, - getNEXSecretKey, - getNEXAESKey, - setNEXPublicKey, - setNEXPrivateKey, - setNEXSecretKey, - setNEXAESKey, - getServicePublicKey, - getServicePrivateKey, - getServiceSecretKey, - getServiceAESKey, - setServicePublicKey, - setServicePrivateKey, - setServiceSecretKey, - setServiceAESKey, - getLocalCDNFile, - setLocalCDNFile -}; \ No newline at end of file diff --git a/src/cache.ts b/src/cache.ts new file mode 100644 index 0000000..593554b --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,61 @@ +import fs from 'fs-extra'; +import redis from 'redis'; +import { config, disabledFeatures } from '@/config-manager'; + +let client: redis.RedisClientType; + +const memoryCache: { [key: string]: Buffer } = {}; + +const LOCAL_CDN_BASE: string = `${__dirname}/../cdn`; + +export async function connect(): Promise { + if (!disabledFeatures.redis) { + client = redis.createClient(config.redis.client); + client.on('error', (err) => console.log('Redis Client Error', err)); + + await client.connect(); + } +} + +export async function setCachedFile(fileName: string, value: Buffer): Promise { + if (disabledFeatures.redis) { + memoryCache[fileName] = value; + } else { + await client.set(fileName, value); + } +} + +export async function getCachedFile(fileName: string, encoding?: BufferEncoding): Promise { + let cachedFile: Buffer = Buffer.alloc(0); + + if (disabledFeatures.redis) { + cachedFile = memoryCache[fileName] || null; + } else { + const redisValue: string | null = await client.get(fileName); + if (redisValue) { + cachedFile = Buffer.from(redisValue, encoding); + } + } + + return cachedFile; +} + +// * Local CDN cache functions + +export async function getLocalCDNFile(name: string, encoding?: BufferEncoding): Promise { + let file: Buffer = await getCachedFile(`local_cdn:${name}`, encoding); + + if (file === null) { + if (await fs.pathExists(`${LOCAL_CDN_BASE}/${name}`)) { + const fileBuffer: string | Buffer = await fs.readFile(`${LOCAL_CDN_BASE}/${name}`, { encoding }); + file = Buffer.from(fileBuffer); + await setLocalCDNFile(name, file); + } + } + + return file; +} + +export async function setLocalCDNFile(name: string, value: Buffer): Promise { + await setCachedFile(`local_cdn:${name}`, value); +} \ No newline at end of file diff --git a/src/config-manager.js b/src/config-manager.js deleted file mode 100644 index 6376355..0000000 --- a/src/config-manager.js +++ /dev/null @@ -1,335 +0,0 @@ -const fs = require('fs-extra'); -const get = require('lodash.get'); -const set = require('lodash.set'); -const logger = require('../logger'); - -require('dotenv').config(); - -/** - * @typedef {Object} Config - * @property {object} http HTTP server settings - * @property {number} http.port HTTP port the server will listen on - * @property {object} mongoose Mongose connection settings - * @property {string} mongoose.connection_string MongoDB connection string - * @property {object} mongoose.options MongoDB connection options - * @property {object} [redis] redis settings - * @property {string} [redis.client] redis client settings - * @property {string} [redis.client.url] redis server URL - * @property {object} [email] node-mailer client settings - * @property {string} [email.host] SMTP server address - * @property {number} [email.port] SMTP server port - * @property {boolean} [email.secure] Secure SMTP - * @property {string} [email.from] Email 'from' name/address - * @property {object} [email.auth] Email authentication settings - * @property {string} [email.auth.user] Email username - * @property {string} [email.auth.pass] Email password - * @property {object} [s3] s3 client settings - * @property {object} [s3.endpoint] s3 endpoint URL - * @property {string} [s3.key] s3 access key - * @property {string} [s3.secret] s3 access secret - * @property {object} [hcaptcha] hCaptcha settings - * @property {string} [hcaptcha.secret] hCaptcha secret - * @property {object} cdn CDN config settings - * @property {object} [cdn.subdomain] Subdomain used for serving CDN contents when s3 is disabled - * @property {string} [cdn.disk_path] Fully qualified file system path for storing and reading local CDN contents - * @property {string} cdn.base_url Base URL for CDN server - * @property {string} website_base Base URL for service website (used with emails) - */ - -/** - * @type {Config} - */ -let config = {}; - -/** - * @typedef {Object} DisabledFeatures - * @property {boolean} redis true if redis is disabled - * @property {boolean} email true if email sending is disabled - * @property {boolean} captcha true if captcha verification is disabled - * @property {boolean} s3 true if s3 services is disabled - */ - -/** - * @type {DisabledFeatures} - */ -const disabledFeatures = { - redis: false, - email: false, - captcha: false, - s3: false -}; - -const requiredFields = [ - ['http.port', 'PN_ACT_CONFIG_HTTP_PORT', Number], - ['mongoose.connection_string', 'PN_ACT_CONFIG_MONGO_CONNECTION_STRING'], - ['cdn.base_url', 'PN_ACT_CONFIG_CDN_BASE_URL'] -]; - -function configure() { - const usingEnv = process.env.PN_ACT_PREFER_ENV_CONFIG === 'true'; - - if (usingEnv) { - logger.info('Loading config from environment variable'); - - config = { - http: { - port: Number(process.env.PN_ACT_CONFIG_HTTP_PORT) - }, - mongoose: { - connection_string: process.env.PN_ACT_CONFIG_MONGO_CONNECTION_STRING, - options: Object.keys(process.env) - .filter(key => key.startsWith('PN_ACT_CONFIG_MONGOOSE_OPTION_')) - .reduce((obj, key) => { - obj[key.split('_').pop()] = process.env[key]; - return obj; - }, {}) - }, - redis: { - client: { - url: process.env.PN_ACT_CONFIG_REDIS_URL - } - }, - email: { - host: process.env.PN_ACT_CONFIG_EMAIL_HOST, - port: Number(process.env.PN_ACT_CONFIG_EMAIL_PORT), - secure: Boolean(process.env.PN_ACT_CONFIG_EMAIL_SECURE), - auth: { - user: process.env.PN_ACT_CONFIG_EMAIL_USERNAME, - pass: process.env.PN_ACT_CONFIG_EMAIL_PASSWORD - }, - from: process.env.PN_ACT_CONFIG_EMAIL_FROM - }, - s3: { - endpoint: process.env.PN_ACT_CONFIG_S3_ENDPOINT, - key: process.env.PN_ACT_CONFIG_S3_ACCESS_KEY, - secret: process.env.PN_ACT_CONFIG_S3_ACCESS_SECRET - }, - hcaptcha: { - secret: process.env.PN_ACT_CONFIG_HCAPTCHA_SECRET - }, - cdn: { - subdomain: process.env.PN_ACT_CONFIG_CDN_SUBDOMAIN, - disk_path: process.env.PN_ACT_CONFIG_CDN_DISK_PATH, - base_url: process.env.PN_ACT_CONFIG_CDN_BASE_URL - }, - website_base: process.env.PN_ACT_CONFIG_WEBSITE_BASE - }; - } else { - logger.info('Loading config from config.json'); - - if (!fs.pathExistsSync(`${__dirname}/../config.json`)) { - logger.error('Failed to locate config.json file'); - process.exit(0); - } - - config = require(`${__dirname}/../config.json`); - } - - logger.info('Config loaded, checking integrity'); - - // * Check for required settings - for (const requiredField of requiredFields) { - const [keyPath, env, convertType] = requiredField; - - const configValue = get(config, keyPath); - const envValue = get(process.env, keyPath); - - if (!configValue || (typeof configValue === 'string' && configValue.trim() === '')) { - if (!envValue || envValue.trim() === '') { - logger.error(`Failed to locate required field ${keyPath}. Set ${keyPath} in config.json or the ${env} environment variable`); - - process.exit(0); - } else { - logger.info(`${keyPath} not found in config, using environment variable ${env}`); - - const newValue = envValue; - - set(config, keyPath, convertType ? convertType(newValue) : newValue); - } - } - } - - // * Check for optional settings - - const redisCheck = get(config, 'redis.client.url'); - - if (!redisCheck || redisCheck.trim() === '') { - if (usingEnv) { - logger.warn('Failed to find Redis connection url. Disabling feature and using in-memory cache. To enable feature set the PN_ACT_CONFIG_REDIS_URL environment variable'); - - } else { - logger.warn('Failed to find Redis connection url. Disabling feature and using in-memory cache. To enable feature set redis.client.url in your config.json'); - - } - - disabledFeatures.redis = true; - } - - const emailHostCheck = get(config, 'email.host'); - - if (!emailHostCheck || emailHostCheck.trim() === '') { - if (usingEnv) { - logger.warn('Failed to find email SMTP host. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_HOST environment variable'); - } else { - logger.warn('Failed to find email SMTP host. Disabling feature. To enable feature set email.host in your config.json'); - } - - - disabledFeatures.email = true; - } - - const emailPortCheck = get(config, 'email.port'); - - if (!emailPortCheck) { - if (usingEnv) { - logger.warn('Failed to find email SMTP port. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_PORT environment variable'); - } else { - logger.warn('Failed to find email SMTP port. Disabling feature. To enable feature set email.port in your config.json'); - } - - disabledFeatures.email = true; - } - - const emailSecureCheck = get(config, 'email.secure'); - - if (emailSecureCheck === undefined) { - if (usingEnv) { - logger.warn('Failed to find email SMTP secure flag. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_SECURE environment variable'); - } else { - - logger.warn('Failed to find email SMTP secure flag. Disabling feature. To enable feature set email.secure in your config.json'); - } - - disabledFeatures.email = true; - } - - const emailUsernameCheck = get(config, 'email.auth.user'); - - if (!emailUsernameCheck || emailUsernameCheck.trim() === '') { - if (usingEnv) { - logger.warn('Failed to find email account username. Disabling feature. To enable feature set the auth.user environment variable'); - } else { - - logger.warn('Failed to find email account username. Disabling feature. To enable feature set email.auth.user in your config.json'); - } - - disabledFeatures.email = true; - } - - const emailPasswordCheck = get(config, 'email.auth.pass'); - - if (!emailPasswordCheck || emailPasswordCheck.trim() === '') { - if (usingEnv) { - logger.warn('Failed to find email account password. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_PASSWORD environment variable'); - } else { - - logger.warn('Failed to find email account password. Disabling feature. To enable feature set email.auth.pass in your config.json'); - } - - disabledFeatures.email = true; - } - - const emailFromCheck = get(config, 'email.from'); - - if (!emailFromCheck || emailFromCheck.trim() === '') { - if (usingEnv) { - logger.warn('Failed to find email from config. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_FROM environment variable'); - } else { - - logger.warn('Failed to find email from config. Disabling feature. To enable feature set email.from in your config.json'); - } - - disabledFeatures.email = true; - } - - if (!disabledFeatures.email) { - const websiteBaseCheck = get(config, 'website_base'); - - if (!websiteBaseCheck || websiteBaseCheck.trim() === '') { - if (usingEnv) { - logger.error('Email sending is enabled and no website base was configured. Set the PN_ACT_CONFIG_WEBSITE_BASE environment variable'); - } else { - logger.error('Email sending is enabled and no website base was configured. Set website_base in your config.json'); - } - - process.exit(0); - } - } - - const captchaSecretCheck = get(config, 'hcaptcha.secret'); - - if (!captchaSecretCheck || captchaSecretCheck.trim() === '') { - if (usingEnv) { - logger.warn('Failed to find captcha secret config. Disabling feature. To enable feature set the PN_ACT_CONFIG_HCAPTCHA_SECRET environment variable'); - } else { - logger.warn('Failed to find captcha secret config. Disabling feature. To enable feature set hcaptcha.secret in your config.json'); - } - - disabledFeatures.captcha = true; - } - - const s3EndpointCheck = get(config, 's3.endpoint'); - - if (!s3EndpointCheck || s3EndpointCheck.trim() === '') { - if (usingEnv) { - logger.warn('Failed to find s3 endpoint config. Disabling feature. To enable feature set the PN_ACT_CONFIG_S3_ENDPOINT environment variable'); - } else { - logger.warn('Failed to find s3 endpoint config. Disabling feature. To enable feature set s3.endpoint in your config.json'); - } - - disabledFeatures.s3 = true; - } else { - } - - const s3AccessKeyCheck = get(config, 's3.key'); - - if (!s3AccessKeyCheck || s3AccessKeyCheck.trim() === '') { - if (usingEnv) { - logger.warn('Failed to find s3 access key config. Disabling feature. To enable feature set the PN_ACT_CONFIG_S3_ACCESS_KEY environment variable'); - } else { - logger.warn('Failed to find s3 access key config. Disabling feature. To enable feature set s3.key in your config.json'); - } - - disabledFeatures.s3 = true; - } - - const s3SecretKeyCheck = get(config, 's3.secret'); - - if (!s3SecretKeyCheck || s3SecretKeyCheck.trim() === '') { - if (usingEnv) { - logger.warn('Failed to find s3 secret key config. Disabling feature. To enable feature set the PN_ACT_CONFIG_S3_ACCESS_SECRET environment variable'); - } else { - logger.warn('Failed to find s3 secret key config. Disabling feature. To enable feature set s3.secret in your config.json'); - } - - disabledFeatures.s3 = true; - } - - if (disabledFeatures.s3) { - const cdnSubdomainCheck = get(config, 'cdn.subdomain'); - - if (!cdnSubdomainCheck || cdnSubdomainCheck.trim() === '') { - if (usingEnv) { - logger.error('s3 file storage is disabled and no CDN subdomain was set. Set the PN_ACT_CONFIG_CDN_SUBDOMAIN environment variable'); - } else { - logger.error('s3 file storage is disabled and no CDN subdomain was set. Set cdn.subdomain in your config.json'); - } - - process.exit(0); - } - - if (disabledFeatures.redis) { - logger.warn('Both s3 and Redis are disabled. Large CDN files will use the in-memory cache, which may result in high memory use. Please enable s3 if you\'re running a production server.'); - } - - logger.warn(`s3 file storage disabled. Using disk-based file storage. Please ensure cdn.base_url config or PN_ACT_CONFIG_CDN_BASE env variable is set to point to this server with the subdomain being ${config.cdn.subdomain}`); - } - - module.exports.config = config; -} - -module.exports = { - configure, - config, - disabledFeatures -}; \ No newline at end of file diff --git a/src/config-manager.ts b/src/config-manager.ts new file mode 100644 index 0000000..23c9f84 --- /dev/null +++ b/src/config-manager.ts @@ -0,0 +1,171 @@ +import fs from 'fs-extra'; +import mongoose from 'mongoose'; +import dotenv from 'dotenv'; +import { LOG_INFO, LOG_WARN, LOG_ERROR } from '@/logger'; +import { Config, DisabledFeatures } from '@/types/common/config'; + +dotenv.config(); + +export const disabledFeatures: DisabledFeatures = { + redis: false, + email: false, + captcha: false, + s3: false +}; + +LOG_INFO('Loading config'); + +let mongooseConnectOptions: mongoose.ConnectOptions = {}; + +if (process.env.PN_ACT_CONFIG_MONGOOSE_CONNECT_OPTIONS_PATH) { + mongooseConnectOptions = fs.readJSONSync(process.env.PN_ACT_CONFIG_MONGOOSE_CONNECT_OPTIONS_PATH); +} + +if (process.env.PN_ACT_CONFIG_EMAIL_SECURE) { + if (process.env.PN_ACT_CONFIG_EMAIL_SECURE !== 'true' && process.env.PN_ACT_CONFIG_EMAIL_SECURE !== 'false') { + LOG_ERROR(`PN_ACT_CONFIG_EMAIL_SECURE must be either true or false, got ${process.env.PN_ACT_CONFIG_EMAIL_SECURE}`); + process.exit(0); + } +} + +export const config: Config = { + http: { + port: Number(process.env.PN_ACT_CONFIG_HTTP_PORT || '') + }, + mongoose: { + connection_string: process.env.PN_ACT_CONFIG_MONGO_CONNECTION_STRING || '', + options: mongooseConnectOptions + }, + redis: { + client: { + url: process.env.PN_ACT_CONFIG_REDIS_URL || '' + } + }, + email: { + host: process.env.PN_ACT_CONFIG_EMAIL_HOST || '', + port: Number(process.env.PN_ACT_CONFIG_EMAIL_PORT || ''), + secure: (process.env.PN_ACT_CONFIG_EMAIL_SECURE || '') === 'true', + auth: { + user: process.env.PN_ACT_CONFIG_EMAIL_USERNAME || '', + pass: process.env.PN_ACT_CONFIG_EMAIL_PASSWORD || '' + }, + from: process.env.PN_ACT_CONFIG_EMAIL_FROM || '' + }, + s3: { + endpoint: process.env.PN_ACT_CONFIG_S3_ENDPOINT || '', + key: process.env.PN_ACT_CONFIG_S3_ACCESS_KEY || '', + secret: process.env.PN_ACT_CONFIG_S3_ACCESS_SECRET || '' + }, + hcaptcha: { + secret: process.env.PN_ACT_CONFIG_HCAPTCHA_SECRET || '' + }, + cdn: { + subdomain: process.env.PN_ACT_CONFIG_CDN_SUBDOMAIN || '', + disk_path: process.env.PN_ACT_CONFIG_CDN_DISK_PATH || '', + base_url: process.env.PN_ACT_CONFIG_CDN_BASE_URL || '' + }, + website_base: process.env.PN_ACT_CONFIG_WEBSITE_BASE || '', + aes_key: process.env.PN_ACT_CONFIG_AES_KEY || '' +}; + +LOG_INFO('Config loaded, checking integrity'); + +if (!config.http.port) { + LOG_ERROR('Failed to find HTTP port. Set the PN_ACT_CONFIG_HTTP_PORT environment variable'); + process.exit(0); +} + +if (!config.mongoose.connection_string) { + LOG_ERROR('Failed to find MongoDB connection string. Set the PN_ACT_CONFIG_MONGO_CONNECTION_STRING environment variable'); + process.exit(0); +} + +if (!config.cdn.base_url) { + LOG_ERROR('Failed to find asset CDN base URL. Set the PN_ACT_CONFIG_CDN_BASE_URL environment variable'); + process.exit(0); +} + +if (!config.redis.client.url) { + LOG_WARN('Failed to find Redis connection url. Disabling feature and using in-memory cache. To enable feature set the PN_ACT_CONFIG_REDIS_URL environment variable'); + disabledFeatures.redis = true; +} + +if (!config.email.host) { + LOG_WARN('Failed to find email SMTP host. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_HOST environment variable'); + disabledFeatures.email = true; +} + +if (!config.email.port) { + LOG_WARN('Failed to find email SMTP port. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_PORT environment variable'); + disabledFeatures.email = true; +} + +if (config.email.secure === undefined) { + LOG_WARN('Failed to find email SMTP secure flag. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_SECURE environment variable'); + disabledFeatures.email = true; +} + +if (!config.email.auth.user) { + LOG_WARN('Failed to find email account username. Disabling feature. To enable feature set the auth.user environment variable'); + disabledFeatures.email = true; +} + +if (!config.email.auth.pass) { + LOG_WARN('Failed to find email account password. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_PASSWORD environment variable'); + disabledFeatures.email = true; +} + +if (!config.email.from) { + LOG_WARN('Failed to find email from config. Disabling feature. To enable feature set the PN_ACT_CONFIG_EMAIL_FROM environment variable'); + disabledFeatures.email = true; +} + +if (!disabledFeatures.email) { + if (!config.website_base) { + LOG_ERROR('Email sending is enabled and no website base was configured. Set the PN_ACT_CONFIG_WEBSITE_BASE environment variable'); + process.exit(0); + } +} + +if (!config.hcaptcha.secret) { + LOG_WARN('Failed to find captcha secret config. Disabling feature. To enable feature set the PN_ACT_CONFIG_HCAPTCHA_SECRET environment variable'); + disabledFeatures.captcha = true; +} + +if (!config.s3.endpoint) { + LOG_WARN('Failed to find s3 endpoint config. Disabling feature. To enable feature set the PN_ACT_CONFIG_S3_ENDPOINT environment variable'); + disabledFeatures.s3 = true; +} + +if (!config.s3.key) { + LOG_WARN('Failed to find s3 access key config. Disabling feature. To enable feature set the PN_ACT_CONFIG_S3_ACCESS_KEY environment variable'); + disabledFeatures.s3 = true; +} + +if (!config.s3.secret) { + LOG_WARN('Failed to find s3 secret key config. Disabling feature. To enable feature set the PN_ACT_CONFIG_S3_ACCESS_SECRET environment variable'); + disabledFeatures.s3 = true; +} + +if (disabledFeatures.s3) { + if (!config.cdn.subdomain) { + LOG_ERROR('s3 file storage is disabled and no CDN subdomain was set. Set the PN_ACT_CONFIG_CDN_SUBDOMAIN environment variable'); + process.exit(0); + } + + if (!config.cdn.disk_path) { + LOG_ERROR('s3 file storage is disabled and no CDN disk path was set. Set the PN_ACT_CONFIG_CDN_DISK_PATH environment variable'); + process.exit(0); + } + + LOG_WARN(`s3 file storage disabled. Using disk-based file storage. Please ensure cdn.base_url config or PN_ACT_CONFIG_CDN_BASE env variable is set to point to this server with the subdomain being ${config.cdn.subdomain}`); + + if (disabledFeatures.redis) { + LOG_WARN('Both s3 and Redis are disabled. Large CDN files will use the in-memory cache, which may result in high memory use. Please enable s3 if you\'re running a production server.'); + } +} + +if (!config.aes_key) { + LOG_ERROR('Token AES key is not set. Set the PN_ACT_CONFIG_AES_KEY environment variable to your AES-256-CBC key'); + process.exit(0); +} \ No newline at end of file diff --git a/src/database.js b/src/database.js deleted file mode 100644 index bfce6ef..0000000 --- a/src/database.js +++ /dev/null @@ -1,274 +0,0 @@ -const mongoose = require('mongoose'); -const bcrypt = require('bcrypt'); -const joi = require('joi'); -const util = require('./util'); -const { PNID } = require('./models/pnid'); -const { Server } = require('./models/server'); -const logger = require('../logger'); -const { config } = require('./config-manager'); -const { connection_string, options } = config.mongoose; - -// TODO: Extend this later with more settings -const discordConnectionSchema = joi.object({ - id: joi.string() -}); - -let connection; - -async function connect() { - await mongoose.connect(connection_string, options); - - connection = mongoose.connection; - connection.on('error', console.error.bind(console, 'connection error:')); - - module.exports.connection = connection; -} - -function verifyConnected() { - if (!connection) { - throw new Error('Cannot make database requets without being connected'); - } -} - -async function getUserByUsername(username) { - verifyConnected(); - - if (typeof username !== 'string') { - return null; - } - - const user = await PNID.findOne({ - usernameLower: username.toLowerCase() - }); - - return user; -} - -async function getUserByPID(pid) { - verifyConnected(); - - const user = await PNID.findOne({ - pid - }); - - return user; -} - -async function getUserByEmailAddress(email) { - verifyConnected(); - - const user = await PNID.findOne({ - 'email.address': new RegExp(email, 'i') // * Ignore case - }); - - return user; -} - -async function doesUserExist(username) { - verifyConnected(); - - return !!await getUserByUsername(username); -} - -async function getUserBasic(token) { - verifyConnected(); - - // Wii U sends Basic auth as `username password`, where the password may not have spaces - // This is not to spec, but that is the consoles fault not ours - const [username, password] = Buffer.from(token, 'base64').toString().split(' '); - const user = await getUserByUsername(username); - - if (!user) { - return null; - } - - const hashedPassword = util.nintendoPasswordHash(password, user.pid); - - if (!bcrypt.compareSync(hashedPassword, user.password)) { - return null; - } - - return user; -} - -async function getUserBearer(token) { - verifyConnected(); - - try { - const decryptedToken = await util.decryptToken(Buffer.from(token, 'base64')); - const unpackedToken = util.unpackToken(decryptedToken); - - const user = await getUserByPID(unpackedToken.pid); - - if (user) { - const expireTime = Math.floor((Number(unpackedToken.expire_time) / 1000)); - - if (Math.floor(Date.now() / 1000) > expireTime) { - return null; - } - } - - return user; - } catch (error) { - // TODO: Handle error - logger.error(error); - return null; - } -} - -async function getUserProfileJSONByPID(pid) { - verifyConnected(); - - const user = await getUserByPID(pid); - const device = user.get('devices')[0]; // Just grab the first device - let device_attributes; - - if (device) { - device_attributes = device.get('device_attributes').map(({name, value, created_date}) => { - const deviceAttributeDocument = { - name, - value - }; - - if (created_date) { - deviceAttributeDocument.created_date = created_date; - } - - return { - device_attribute: deviceAttributeDocument - }; - }); - } - - const userObject = { - //accounts: {}, We need to figure this out, no idea what these values mean or what they do - active_flag: user.get('flags.active') ? 'Y' : 'N', - birth_date: user.get('birthdate'), - country: user.get('country'), - create_date: user.get('creation_date'), - device_attributes: device_attributes, - gender: user.get('gender'), - language: user.get('language'), - updated: user.get('updated'), - marketing_flag: user.get('flags.marketing') ? 'Y' : 'N', - off_device_flag: user.get('flags.off_device') ? 'Y' : 'N', - pid: user.get('pid'), - email: { - address: user.get('email.address'), - id: user.get('email.id'), - parent: user.get('email.parent') ? 'Y' : 'N', - primary: user.get('email.primary') ? 'Y' : 'N', - reachable: user.get('email.reachable') ? 'Y' : 'N', - type: 'DEFAULT', - updated_by: 'USER', // Can also be INTERNAL WS, don't know the difference - validated: user.get('email.validated') ? 'Y' : 'N' - }, - mii: { - status: 'COMPLETED', - data: user.get('mii.data').replace(/(\r\n|\n|\r)/gm, ''), - id: user.get('mii.id'), - mii_hash: user.get('mii.hash'), - mii_images: { - mii_image: { - // Images MUST be loaded over HTTPS or console ignores them - // Bunny CDN is the only CDN which seems to support TLS 1.0/1.1 (required) - cached_url: `${config.cdn.base_url}/mii/${user.pid}/standard.tga`, - id: user.get('mii.image_id'), - url: `${config.cdn.base_url}/mii/${user.pid}/standard.tga`, - type: 'standard' - } - }, - name: user.get('mii.name'), - primary: user.get('mii.primary') ? 'Y' : 'N', - }, - region: user.get('region'), - tz_name: user.get('timezone.name'), - user_id: user.get('username'), - utc_offset: user.get('timezone.offset') - }; - - if (user.get('email.validated')) { - userObject.email.validated_date = user.get('email.validated_date'); - } - - return userObject; -} - -function getServer(gameServerId, accessMode) { - return Server.findOne({ - game_server_id: gameServerId, - access_mode: accessMode, - }); -} - -function getServerByTitleId(titleId, accessMode) { - return Server.findOne({ - title_ids: titleId, - access_mode: accessMode, - }); -} - -async function addUserConnection(pnid, data, type) { - if (type === 'discord') { - return await addUserConnectionDiscord(pnid, data); - } -} - -async function addUserConnectionDiscord(pnid, data) { - const valid = discordConnectionSchema.validate(data); - - if (valid.error) { - return { - app: 'api', - status: 400, - error: 'Invalid or missing connection data' - }; - } - - await PNID.updateOne({ pid: pnid.get('pid') }, { - $set: { - 'connections.discord.id': data.id - } - }); - - return { - app: 'api', - status: 200 - }; -} - -async function removeUserConnection(pnid, type) { - // Add more connections later? - if (type === 'discord') { - return await removeUserConnectionDiscord(pnid); - } -} - -async function removeUserConnectionDiscord(pnid) { - await PNID.updateOne({ pid: pnid.get('pid') }, { - $set: { - 'connections.discord.id': '' - } - }); - - return { - app: 'api', - status: 200 - }; -} - -module.exports = { - connect, - connection, - getUserByUsername, - getUserByPID, - getUserByEmailAddress, - doesUserExist, - getUserBasic, - getUserBearer, - getUserProfileJSONByPID, - getServer, - getServerByTitleId, - addUserConnection, - removeUserConnection, -}; \ No newline at end of file diff --git a/src/database.ts b/src/database.ts new file mode 100644 index 0000000..e7f7748 --- /dev/null +++ b/src/database.ts @@ -0,0 +1,272 @@ +import mongoose from 'mongoose'; +import bcrypt from 'bcrypt'; +import joi from 'joi'; +import { nintendoPasswordHash, decryptToken, unpackToken } from '@/util'; +import { PNID } from '@/models/pnid'; +import { Server } from '@/models/server'; +import { LOG_ERROR } from '@/logger'; +import { config } from '@/config-manager'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; +import { IDevice } from '@/types/mongoose/device'; +import { IDeviceAttribute } from '@/types/mongoose/device-attribute'; +import { HydratedServerDocument } from '@/types/mongoose/server'; +import { Token } from '@/types/common/token'; +import { PNIDProfile } from '@/types/services/nnid/pnid-profile'; +import { ConnectionData } from '@/types/services/api/connection-data'; +import { ConnectionResponse } from '@/types/services/api/connection-response'; +import { DiscordConnectionData } from '@/types/services/api/discord-connection-data'; + +const connection_string: string = config.mongoose.connection_string; +const options: mongoose.ConnectOptions = config.mongoose.options; + +// TODO: Extend this later with more settings +const discordConnectionSchema: joi.ObjectSchema = joi.object({ + id: joi.string() +}); + +let _connection: mongoose.Connection; + +export async function connect(): Promise { + await mongoose.connect(connection_string, options); + + _connection = mongoose.connection; + _connection.on('error', console.error.bind(console, 'connection error:')); +} + +export function connection(): mongoose.Connection { + return _connection; +} + +export function verifyConnected(): void { + if (!connection()) { + throw new Error('Cannot make database requets without being connected'); + } +} + +export async function getPNIDByUsername(username: string): Promise { + verifyConnected(); + + return await PNID.findOne({ + usernameLower: username.toLowerCase() + }); +} + +export async function getPNIDByPID(pid: number): Promise { + verifyConnected(); + + return await PNID.findOne({ + pid + }); +} + +export async function getPNIDByEmailAddress(email: string): Promise { + verifyConnected(); + + // TODO - Update documents to store email normalized + return await PNID.findOne({ + 'email.address': email + }); +} + +export async function doesPNIDExist(username: string): Promise { + verifyConnected(); + + return !!await getPNIDByUsername(username); +} + +export async function getPNIDByBasicAuth(token: string): Promise { + verifyConnected(); + + // * Wii U sends Basic auth as `username password`, where the password may not have spaces + // * This is not to spec, but that is the consoles fault not ours + const decoded: string = Buffer.from(token, 'base64').toString(); + const parts: string[] = decoded.split(' '); + + const username: string = parts[0]; + const password: string = parts[1]; + + const pnid: HydratedPNIDDocument | null = await getPNIDByUsername(username); + + if (!pnid) { + return null; + } + + const hashedPassword: string = nintendoPasswordHash(password, pnid.pid); + + if (!bcrypt.compareSync(hashedPassword, pnid.password)) { + return null; + } + + return pnid; +} + +export async function getPNIDByBearerAuth(token: string): Promise { + verifyConnected(); + + try { + const decryptedToken: Buffer = await decryptToken(config.aes_key, Buffer.from(token, 'hex')); + const unpackedToken: Token = unpackToken(decryptedToken); + + const pnid: HydratedPNIDDocument | null = await getPNIDByPID(unpackedToken.pid); + + if (pnid) { + const expireTime: number = Math.floor((Number(unpackedToken.expire_time) / 1000)); + + if (Math.floor(Date.now() / 1000) > expireTime) { + return null; + } + } + + return pnid; + } catch (error: any) { + // TODO: Handle error + LOG_ERROR(error); + return null; + } +} + +export async function getPNIDProfileJSONByPID(pid: number): Promise { + verifyConnected(); + + const pnid: HydratedPNIDDocument | null = await getPNIDByPID(pid); + + if (!pnid) { + return null; + } + + const device: IDevice = pnid.devices[0]; // * Just grab the first device + let device_attributes: { + device_attribute: { + name: string; + value: string; + created_date: string; + }; + }[] = []; + + if (device) { + device_attributes = device.device_attributes.map((attribute: IDeviceAttribute) => { + const name: string = attribute.name; + const value: string = attribute.value; + const created_date: string | undefined = attribute.created_date; + + return { + device_attribute: { + name, + value, + created_date: created_date ? created_date : '' + } + }; + }); + } + + return { + //accounts: {}, // * We need to figure this out, no idea what these values mean or what they do + active_flag: pnid.flags.active ? 'Y' : 'N', + birth_date: pnid.birthdate, + country: pnid.country, + create_date: pnid.creation_date, + device_attributes: device_attributes, + gender: pnid.gender, + language: pnid.language, + updated: pnid.updated, + marketing_flag: pnid.flags.marketing ? 'Y' : 'N', + off_device_flag: pnid.flags.off_device ? 'Y' : 'N', + pid: pnid.pid, + email: { + address: pnid.email.address, + id: pnid.email.id, + parent: pnid.email.parent ? 'Y' : 'N', + primary: pnid.email.primary ? 'Y' : 'N', + reachable: pnid.email.reachable ? 'Y' : 'N', + type: 'DEFAULT', + updated_by: 'USER', // * Can also be INTERNAL WS, don't know the difference + validated: pnid.email.validated ? 'Y' : 'N', + validated_date: pnid.email.validated ? pnid.email.validated_date : '' + }, + mii: { + status: 'COMPLETED', + data: pnid.mii.data.replace(/(\r\n|\n|\r)/gm, ''), + id: pnid.mii.id, + mii_hash: pnid.mii.hash, + mii_images: { + mii_image: { + // * Images MUST be loaded over HTTPS or console ignores them + // * Bunny CDN is the only CDN which seems to support TLS 1.0/1.1 (required) + cached_url: `${config.cdn.base_url}/mii/${pnid.pid}/standard.tga`, + id: pnid.mii.image_id, + url: `${config.cdn.base_url}/mii/${pnid.pid}/standard.tga`, + type: 'standard' + } + }, + name: pnid.mii.name, + primary: pnid.mii.primary ? 'Y' : 'N', + }, + region: pnid.region, + tz_name: pnid.timezone.name, + user_id: pnid.username, + utc_offset: pnid.timezone.offset + }; +} + +export async function getServerByGameServerId(gameServerId: string, accessMode: string): Promise { + return await Server.findOne({ + game_server_id: gameServerId, + access_mode: accessMode + }); +} + +export async function getServerByTitleId(titleId: string, accessMode: string): Promise { + return await Server.findOne({ + title_ids: titleId, + access_mode: accessMode + }); +} + +export async function addPNIDConnection(pnid: HydratedPNIDDocument, data: ConnectionData, type: string): Promise { + if (type === 'discord') { + return await addPNIDConnectionDiscord(pnid, data); + } +} + +export async function addPNIDConnectionDiscord(pnid: HydratedPNIDDocument, data: DiscordConnectionData): Promise { + const valid: joi.ValidationResult = discordConnectionSchema.validate(data); + + if (valid.error) { + return { + app: 'api', + status: 400, + error: 'Invalid or missing connection data' + }; + } + + await PNID.updateOne({ pid: pnid.pid }, { + $set: { + 'connections.discord.id': data.id + } + }); + + return { + app: 'api', + status: 200 + }; +} + +export async function removePNIDConnection(pnid: HydratedPNIDDocument, type: string): Promise { + // * Add more connections later? + if (type === 'discord') { + return await removePNIDConnectionDiscord(pnid); + } +} + +export async function removePNIDConnectionDiscord(pnid: HydratedPNIDDocument): Promise { + await PNID.updateOne({ pid: pnid.pid }, { + $set: { + 'connections.discord.id': '' + } + }); + + return { + app: 'api', + status: 200 + }; +} \ No newline at end of file diff --git a/logger.js b/src/logger.ts similarity index 64% rename from logger.js rename to src/logger.ts index 07215de..917b677 100644 --- a/logger.js +++ b/src/logger.ts @@ -1,7 +1,9 @@ -const fs = require('fs-extra'); -require('colors'); +import fs from 'fs-extra'; +import colors from 'colors'; -const root = __dirname; +colors.enable(); + +const root: string = process.env.PN_ACT_LOGGER_PATH ? process.env.PN_ACT_LOGGER_PATH : `${__dirname}/..`; fs.ensureDirSync(`${root}/logs`); const streams = { @@ -10,43 +12,36 @@ const streams = { error: fs.createWriteStream(`${root}/logs/error.log`), warn: fs.createWriteStream(`${root}/logs/warn.log`), info: fs.createWriteStream(`${root}/logs/info.log`) -}; +} as const; -function success(input) { - const time = new Date(); +export function LOG_SUCCESS(input: string): void { + const time: Date = new Date(); input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [SUCCESS]: ${input}`; streams.success.write(`${input}\n`); console.log(`${input}`.green.bold); } -function error(input) { - const time = new Date(); +export function LOG_ERROR(input: string): void { + const time: Date = new Date(); input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [ERROR]: ${input}`; streams.error.write(`${input}\n`); console.log(`${input}`.red.bold); } -function warn(input) { - const time = new Date(); +export function LOG_WARN(input: string): void { + const time: Date = new Date(); input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [WARN]: ${input}`; streams.warn.write(`${input}\n`); console.log(`${input}`.yellow.bold); } -function info(input) { - const time = new Date(); +export function LOG_INFO(input: string): void { + const time: Date = new Date(); input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [INFO]: ${input}`; streams.info.write(`${input}\n`); console.log(`${input}`.cyan.bold); -} - -module.exports = { - success, - error, - warn, - info -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/mailer.js b/src/mailer.js deleted file mode 100644 index 7292f6e..0000000 --- a/src/mailer.js +++ /dev/null @@ -1,65 +0,0 @@ -const nodemailer = require('nodemailer'); -const { config, disabledFeatures } = require('./config-manager'); -const path = require('path'); -const fs = require("fs"); -const genericEmailTemplate = fs.readFileSync(path.join(__dirname, './assets/emails/genericTemplate.html'), 'utf8'); -const confirmationEmailTemplate = fs.readFileSync(path.join(__dirname, './assets/emails/confirmationTemplate.html'), 'utf8'); - -let transporter; - -if (!disabledFeatures.email) { - transporter = nodemailer.createTransport(config.email); -} - -/** - @param {Object} options - @param {String} options.to The address of the recipient - @param {String} options.subject The subject of the email - - @param {String} options.username The username of the user (shown in the greeting) - @param {String} options.preview The preview text of the email (shown in the inbox by the email client) - @param {String} options.text The text version of the email - - @param {String} options.paragraph The main content of the email - - @param {Object} options.confirmation Whether or not the email is a confirmation email - @param {String} options.confirmation.href The link to the confirmation page - @param {String} options.confirmation.code The confirmation code - - @param {Object} options.link An object containing the link to be shown in the email - @param {String} options.link.href The URL of the link - @param {String} options.link.text The text of the link - -*/ -async function sendMail(options) { - if (!disabledFeatures.email) { - const { to, subject, username, paragraph, preview, text, link, confirmation } = options; - - let html = confirmation ? confirmationEmailTemplate : genericEmailTemplate; - - html = html.replace(/{{username}}/g, username); - html = html.replace(/{{paragraph}}/g, paragraph); - html = html.replace(/{{preview}}/g, (preview || "")); - html = html.replace(/{{confirmation-href}}/g, (confirmation?.href || "")); - html = html.replace(/{{confirmation-code}}/g, (confirmation?.code || "")); - - if (link) { - const { href, text } = link; - - const button = ` ${text}` - html = html.replace(//g, button); - } - - await transporter.sendMail({ - from: config.email.from, - to, - subject, - text, - html - }); - } -} - -module.exports = { - sendMail -}; diff --git a/src/mailer.ts b/src/mailer.ts new file mode 100644 index 0000000..230d412 --- /dev/null +++ b/src/mailer.ts @@ -0,0 +1,43 @@ +import path from 'node:path'; +import fs from 'node:fs'; +import nodemailer from 'nodemailer'; +import { config, disabledFeatures } from '@/config-manager'; +import { MailerOptions } from '@/types/common/mailer-options'; + +const genericEmailTemplate: string = fs.readFileSync(path.join(__dirname, './assets/emails/genericTemplate.html'), 'utf8'); +const confirmationEmailTemplate: string = fs.readFileSync(path.join(__dirname, './assets/emails/confirmationTemplate.html'), 'utf8'); + +let transporter: nodemailer.Transporter; + +if (!disabledFeatures.email) { + transporter = nodemailer.createTransport(config.email); +} + +export async function sendMail(options: MailerOptions): Promise { + if (!disabledFeatures.email) { + const { to, subject, username, paragraph, preview, text, link, confirmation } = options; + + let html: string = confirmation ? confirmationEmailTemplate : genericEmailTemplate; + + html = html.replace(/{{username}}/g, username); + html = html.replace(/{{paragraph}}/g, paragraph || ''); + html = html.replace(/{{preview}}/g, preview || ''); + html = html.replace(/{{confirmation-href}}/g, confirmation?.href || ''); + html = html.replace(/{{confirmation-code}}/g, confirmation?.code || ''); + + if (link) { + const { href, text } = link; + + const button: string = ` ${text}`; + html = html.replace(//g, button); + } + + await transporter.sendMail({ + from: config.email.from, + to, + subject, + text, + html + }); + } +} diff --git a/src/middleware/api.js b/src/middleware/api.js deleted file mode 100644 index d6489d1..0000000 --- a/src/middleware/api.js +++ /dev/null @@ -1,19 +0,0 @@ -const xmlbuilder = require('xmlbuilder'); -const database = require('../database'); - -async function APIMiddleware(request, response, next) { - const { headers } = request; - - if (!headers.authorization || !(headers.authorization.startsWith('Bearer'))) { - return next(); - } - - const token = headers.authorization.split(' ')[1]; - const user = await database.getUserBearer(token); - - request.pnid = user; - - return next(); -} - -module.exports = APIMiddleware; \ No newline at end of file diff --git a/src/middleware/api.ts b/src/middleware/api.ts new file mode 100644 index 0000000..8645559 --- /dev/null +++ b/src/middleware/api.ts @@ -0,0 +1,21 @@ +import express from 'express'; +import { getValueFromHeaders } from '@/util'; +import { getPNIDByBearerAuth } from '@/database'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +async function APIMiddleware(request: express.Request, _response: express.Response, next: express.NextFunction): Promise { + const authHeader: string | undefined = getValueFromHeaders(request.headers, 'authorization'); + + if (!authHeader || !(authHeader.startsWith('Bearer'))) { + return next(); + } + + const token: string = authHeader.split(' ')[1]; + const pnid: HydratedPNIDDocument | null = await getPNIDByBearerAuth(token); + + request.pnid = pnid; + + return next(); +} + +export default APIMiddleware; \ No newline at end of file diff --git a/src/middleware/cemu.js b/src/middleware/cemu.js deleted file mode 100644 index c6e8441..0000000 --- a/src/middleware/cemu.js +++ /dev/null @@ -1,9 +0,0 @@ -async function CemuMiddleware(request, response, next) { - const subdomain = request.subdomains.reverse().join('.'); - - request.isCemu = subdomain === 'c.account'; - - return next(); -} - -module.exports = CemuMiddleware; \ No newline at end of file diff --git a/src/middleware/cemu.ts b/src/middleware/cemu.ts new file mode 100644 index 0000000..dc44985 --- /dev/null +++ b/src/middleware/cemu.ts @@ -0,0 +1,11 @@ +import express from 'express'; + +function CemuMiddleware(request: express.Request, _response: express.Response, next: express.NextFunction): void { + const subdomain: string = request.subdomains.reverse().join('.'); + + request.isCemu = subdomain === 'c.account'; + + return next(); +} + +export default CemuMiddleware; \ No newline at end of file diff --git a/src/middleware/client-header.js b/src/middleware/client-header.js deleted file mode 100644 index 1999223..0000000 --- a/src/middleware/client-header.js +++ /dev/null @@ -1,38 +0,0 @@ -const xmlbuilder = require('xmlbuilder'); - -const VALID_CLIENT_ID_SECRET_PAIRS = { - // 'Key' is the client ID, 'Value' is the client secret - 'a2efa818a34fa16b8afbc8a74eba3eda': 'c91cdb5658bd4954ade78533a339cf9a', // Possibly WiiU exclusive? - 'daf6227853bcbdce3d75baee8332b': '3eff548eac636e2bf45bb7b375e7b6b0', // Possibly 3DS exclusive? - 'ea25c66c26b403376b4c5ed94ab9cdea': 'd137be62cb6a2b831cad8c013b92fb55', // Possibly 3DS exclusive? -}; - - -function nintendoClientHeaderCheck(request, response, next) { - response.set('Content-Type', 'text/xml'); - response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', new Date().getTime()); - - const {headers} = request; - - if ( - !headers['x-nintendo-client-id'] || - !headers['x-nintendo-client-secret'] || - !VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] || - headers['x-nintendo-client-secret'] !== VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] - ) { - return response.send(xmlbuilder.create({ - errors: { - error: { - cause: 'client_id', - code: '0004', - message: 'API application invalid or incorrect application credentials' - } - } - }).end()); - } - - return next(); -} - -module.exports = nintendoClientHeaderCheck; \ No newline at end of file diff --git a/src/middleware/client-header.ts b/src/middleware/client-header.ts new file mode 100644 index 0000000..fc9c1e5 --- /dev/null +++ b/src/middleware/client-header.ts @@ -0,0 +1,43 @@ +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import { getValueFromHeaders } from '@/util'; + +const VALID_CLIENT_ID_SECRET_PAIRS: { [key: string]: string } = { + // * 'Key' is the client ID, 'Value' is the client secret + 'a2efa818a34fa16b8afbc8a74eba3eda': 'c91cdb5658bd4954ade78533a339cf9a', // * Possibly WiiU exclusive? + 'daf6227853bcbdce3d75baee8332b': '3eff548eac636e2bf45bb7b375e7b6b0', // * Possibly 3DS exclusive? + 'ea25c66c26b403376b4c5ed94ab9cdea': 'd137be62cb6a2b831cad8c013b92fb55', // * Possibly 3DS exclusive? +}; + + +function nintendoClientHeaderCheck(request: express.Request, response: express.Response, next: express.NextFunction): void { + response.set('Content-Type', 'text/xml'); + response.set('Server', 'Nintendo 3DS (http)'); + response.set('X-Nintendo-Date', new Date().getTime().toString()); + + const clientId: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-client-id'); + const clientSecret: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-client-secret'); + + if ( + !clientId || + !clientSecret || + !VALID_CLIENT_ID_SECRET_PAIRS[clientId] || + clientSecret !== VALID_CLIENT_ID_SECRET_PAIRS[clientId] + ) { + response.send(xmlbuilder.create({ + errors: { + error: { + cause: 'client_id', + code: '0004', + message: 'API application invalid or incorrect application credentials' + } + } + }).end()); + + return; + } + + return next(); +} + +export default nintendoClientHeaderCheck; \ No newline at end of file diff --git a/src/middleware/device-certificate.js b/src/middleware/device-certificate.js deleted file mode 100644 index 7598091..0000000 --- a/src/middleware/device-certificate.js +++ /dev/null @@ -1,16 +0,0 @@ -const NintendoCertificate = require('../nintendo-certificate'); - -async function deviceCertificateMiddleware(request, response, next) { - const { headers } = request; - - if (!headers['x-nintendo-device-cert']) { - return next(); - } - - const certificate = headers['x-nintendo-device-cert']; - request.certificate = new NintendoCertificate(certificate); - - return next(); -} - -module.exports = deviceCertificateMiddleware; \ No newline at end of file diff --git a/src/middleware/device-certificate.ts b/src/middleware/device-certificate.ts new file mode 100644 index 0000000..9e7d224 --- /dev/null +++ b/src/middleware/device-certificate.ts @@ -0,0 +1,17 @@ +import express from 'express'; +import NintendoCertificate from '@/nintendo-certificate'; +import { getValueFromHeaders } from '@/util'; + +function deviceCertificateMiddleware(request: express.Request, _response: express.Response, next: express.NextFunction): void { + const certificate: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-device-cert'); + + if (!certificate) { + return next(); + } + + request.certificate = new NintendoCertificate(certificate); + + return next(); +} + +export default deviceCertificateMiddleware; \ No newline at end of file diff --git a/src/middleware/nasc.js b/src/middleware/nasc.ts similarity index 52% rename from src/middleware/nasc.js rename to src/middleware/nasc.ts index 4bb91a9..5b4da42 100644 --- a/src/middleware/nasc.js +++ b/src/middleware/nasc.ts @@ -1,12 +1,18 @@ -const crypto = require('crypto'); -const { Device } = require('../models/device'); -const { NEXAccount } = require('../models/nex-account'); -const util = require('../util'); -const database = require('../database'); -const NintendoCertificate = require('../nintendo-certificate'); +import crypto from 'node:crypto'; +import express from 'express'; +import mongoose from 'mongoose'; +import { Device } from '@/models/device'; +import { NEXAccount } from '@/models/nex-account'; +import { nascError, nintendoBase64Decode } from '@/util'; +import { connection as databaseConnection } from '@/database'; +import NintendoCertificate from '@/nintendo-certificate'; +import { LOG_ERROR } from '@/logger'; +import { NASCRequestParams } from '@/types/services/nasc/request-params'; +import { HydratedNEXAccountDocument } from '@/types/mongoose/nex-account'; +import { HydratedDeviceDocument } from '@/types/mongoose/device'; -async function NASCMiddleware(request, response, next) { - const requestParams = request.body; +async function NASCMiddleware(request: express.Request, response: express.Response, next: express.NextFunction): Promise { + const requestParams: NASCRequestParams = request.body; if (!requestParams.action || !requestParams.fcdcert || @@ -15,50 +21,54 @@ async function NASCMiddleware(request, response, next) { !requestParams.titleid || !requestParams.servertype ) { - return response.status(200).send(util.nascError('null')); // This is what Nintendo sends + response.status(200).send(nascError('null').toString()); // This is what Nintendo sends + return; } - const action = util.nintendoBase64Decode(requestParams.action).toString(); - const fcdcert = util.nintendoBase64Decode(requestParams.fcdcert); - const serialNumber = util.nintendoBase64Decode(requestParams.csnum).toString(); - const macAddress = util.nintendoBase64Decode(requestParams.macadr).toString(); - const titleID = util.nintendoBase64Decode(requestParams.titleid).toString(); - const environment = util.nintendoBase64Decode(requestParams.servertype).toString(); + const action: string = nintendoBase64Decode(requestParams.action).toString(); + const fcdcert: Buffer = nintendoBase64Decode(requestParams.fcdcert); + const serialNumber: string = nintendoBase64Decode(requestParams.csnum).toString(); + const macAddress: string = nintendoBase64Decode(requestParams.macadr).toString(); + const titleID: string = nintendoBase64Decode(requestParams.titleid).toString(); + const environment: string = nintendoBase64Decode(requestParams.servertype).toString(); - const macAddressHash = crypto.createHash('sha256').update(macAddress).digest('base64'); - const fcdcertHash = crypto.createHash('sha256').update(fcdcert).digest('base64'); + const macAddressHash: string = crypto.createHash('sha256').update(macAddress).digest('base64'); + const fcdcertHash: string = crypto.createHash('sha256').update(fcdcert).digest('base64'); - let pid; - let pidHmac; - let password; + let pid: number = 0; // * Real PIDs are always positive and non-zero + let pidHmac: string = ''; + let password: string = ''; if (requestParams.userid) { - pid = util.nintendoBase64Decode(requestParams.userid).toString(); + pid = Number(nintendoBase64Decode(requestParams.userid).toString()); } if (requestParams.uidhmac) { - pidHmac = util.nintendoBase64Decode(requestParams.uidhmac).toString(); + pidHmac = nintendoBase64Decode(requestParams.uidhmac).toString(); } if (requestParams.passwd) { - password = util.nintendoBase64Decode(requestParams.passwd).toString(); + password = nintendoBase64Decode(requestParams.passwd).toString(); } if (action !== 'LOGIN' && action !== 'SVCLOC') { - return response.status(200).send(util.nascError('null')); // This is what Nintendo sends + response.status(200).send(nascError('null').toString()); // This is what Nintendo sends + return; } - const cert = new NintendoCertificate(fcdcert); - + const cert: NintendoCertificate = new NintendoCertificate(fcdcert); + if (!cert.valid) { - return response.status(200).send(util.nascError('121')); + response.status(200).send(nascError('121').toString()); + return; } if (!validNintendoMACAddress(macAddress)) { - return response.status(200).send(util.nascError('null')); + response.status(200).send(nascError('null').toString()); + return; } - let model; + let model: string = ''; switch (serialNumber[0]) { case 'C': model = 'ctr'; @@ -81,10 +91,11 @@ async function NASCMiddleware(request, response, next) { } if (!model) { - return response.status(200).send(util.nascError('null')); + response.status(200).send(nascError('null').toString()); + return; } - let device = await Device.findOne({ + let device: HydratedDeviceDocument | null = await Device.findOne({ model, serial: serialNumber, environment, @@ -94,14 +105,16 @@ async function NASCMiddleware(request, response, next) { if (device) { if (device.get('access_level') < 0) { - return response.status(200).send(util.nascError('102')); + response.status(200).send(nascError('102').toString()); + return; } if (pid) { const linkedPIDs = device.get('linked_pids'); if (!linkedPIDs.includes(pid)) { - return response.status(200).send(util.nascError('102')); + response.status(200).send(nascError('102').toString()); + return; } } } @@ -110,12 +123,12 @@ async function NASCMiddleware(request, response, next) { if (password && !pid && !pidHmac) { // Register new user - const session = await database.connection.startSession(); + const session: mongoose.ClientSession = await databaseConnection().startSession(); await session.startTransaction(); try { // Create new NEX account - const nexAccount = await new NEXAccount({ + const nexAccount: HydratedNEXAccountDocument = new NEXAccount({ device_type: '3ds', password }); @@ -124,7 +137,7 @@ async function NASCMiddleware(request, response, next) { await nexAccount.save({ session }); - pid = nexAccount.get('pid'); + pid = nexAccount.pid; // Set password @@ -146,12 +159,13 @@ async function NASCMiddleware(request, response, next) { await session.commitTransaction(); } catch (error) { - logger.error('[NASC] REGISTER ACCOUNT: ' + error); + LOG_ERROR('[NASC] REGISTER ACCOUNT: ' + error); await session.abortTransaction(); // 3DS expects 200 even on error - return response.status(200).send(util.nascError('102')); + response.status(200).send(nascError('102').toString()); + return; } finally { // * This runs regardless of failure // * Returning on catch will not prevent this from running @@ -160,20 +174,21 @@ async function NASCMiddleware(request, response, next) { } } - const nexUser = await NEXAccount.findOne({ pid }); + const nexAccount: HydratedNEXAccountDocument | null = await NEXAccount.findOne({ pid }); - if (!nexUser || nexUser.get('access_level') < 0) { - return response.status(200).send(util.nascError('102')); + if (!nexAccount || nexAccount.access_level < 0) { + response.status(200).send(nascError('102').toString()); + return; } - request.nexUser = nexUser; + request.nexAccount = nexAccount; return next(); } // https://www.adminsub.net/mac-address-finder/nintendo // Saves us from doing an OUI lookup each time -const NINTENDO_VENDER_OUIS = [ +const NINTENDO_VENDER_OUIS: string[] = [ 'ECC40D', 'E84ECE', 'E0F6B5', 'E0E751', 'E00C7F', 'DC68EB', 'D86BF7', 'D4F057', 'CCFB65', 'CC9E00', 'B8AE6E', 'B88AEC', 'B87826', 'A4C0E1', 'A45C27', 'A438CC', '9CE635', '98E8FA', @@ -199,10 +214,10 @@ const NINTENDO_VENDER_OUIS = [ ]; // TODO: Make something better -const MAC_REGEX = /^[0-9a-fA-F]{12}$/; +const MAC_REGEX: RegExp = /^[0-9a-fA-F]{12}$/; // Maybe should later parse more data out -function validNintendoMACAddress(macAddress) { +function validNintendoMACAddress(macAddress: string): boolean { if (!NINTENDO_VENDER_OUIS.includes(macAddress.substring(0, 6).toUpperCase())) { return false; } @@ -210,4 +225,4 @@ function validNintendoMACAddress(macAddress) { return MAC_REGEX.test(macAddress); } -module.exports = NASCMiddleware; \ No newline at end of file +export default NASCMiddleware; \ No newline at end of file diff --git a/src/middleware/pnid.js b/src/middleware/pnid.js deleted file mode 100644 index d28d745..0000000 --- a/src/middleware/pnid.js +++ /dev/null @@ -1,65 +0,0 @@ -const xmlbuilder = require('xmlbuilder'); -const database = require('../database'); - -async function PNIDMiddleware(request, response, next) { - const { headers } = request; - - if (!headers.authorization || !(headers.authorization.startsWith('Bearer') || headers.authorization.startsWith('Basic'))) { - return next(); - } - - let [type, token] = headers.authorization.split(' '); - let user; - - if (request.isCemu) { - token = Buffer.from(token, 'hex').toString('base64'); - } - - if (type === 'Basic') { - user = await database.getUserBasic(token); - } else { - user = await database.getUserBearer(token); - } - - if (!user) { - response.status(401); - - if (type === 'Bearer') { - return response.send(xmlbuilder.create({ - errors: { - error: { - cause: 'access_token', - code: '0005', - message: 'Invalid access token' - } - } - }).end()); - } - - return response.send(xmlbuilder.create({ - errors: { - error: { - code: '1105', - message: 'Email address, username, or password, is not valid' - } - } - }).end()); - } - - if (user.get('access_level') < 0) { - return response.status(400).send(xmlbuilder.create({ - errors: { - error: { - code: '0122', - message: 'Device has been banned by game server' - } - } - }).end()); - } - - request.pnid = user; - - return next(); -} - -module.exports = PNIDMiddleware; \ No newline at end of file diff --git a/src/middleware/pnid.ts b/src/middleware/pnid.ts new file mode 100644 index 0000000..3ab6d96 --- /dev/null +++ b/src/middleware/pnid.ts @@ -0,0 +1,76 @@ +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import { getValueFromHeaders } from '@/util'; +import { getPNIDByBasicAuth, getPNIDByBearerAuth } from '@/database'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +async function PNIDMiddleware(request: express.Request, response: express.Response, next: express.NextFunction): Promise { + const authHeader: string | undefined = getValueFromHeaders(request.headers, 'authorization'); + + if (!authHeader || !(authHeader.startsWith('Bearer') || authHeader.startsWith('Basic'))) { + return next(); + } + + const parts: string[] = authHeader.split(' '); + const type: string = parts[0]; + let token: string = parts[1]; + let pnid: HydratedPNIDDocument | null; + + if (request.isCemu) { + token = Buffer.from(token, 'hex').toString('base64'); + } + + if (type === 'Basic') { + pnid = await getPNIDByBasicAuth(token); + } else { + pnid = await getPNIDByBearerAuth(token); + } + + if (!pnid) { + response.status(401); + + if (type === 'Bearer') { + response.send(xmlbuilder.create({ + errors: { + error: { + cause: 'access_token', + code: '0005', + message: 'Invalid access token' + } + } + }).end()); + + return; + } + + response.send(xmlbuilder.create({ + errors: { + error: { + code: '1105', + message: 'Email address, username, or password, is not valid' + } + } + }).end()); + + return; + } + + if (pnid.access_level < 0) { + response.status(400).send(xmlbuilder.create({ + errors: { + error: { + code: '0122', + message: 'Device has been banned by game server' + } + } + }).end()); + + return; + } + + request.pnid = pnid; + + return next(); +} + +export default PNIDMiddleware; \ No newline at end of file diff --git a/src/middleware/ratelimit.js b/src/middleware/ratelimit.js deleted file mode 100644 index 0951794..0000000 --- a/src/middleware/ratelimit.js +++ /dev/null @@ -1,11 +0,0 @@ -const crypto = require('crypto'); -const ratelimit = require('express-rate-limit'); - -module.exports = ratelimit({ - windowMs: 60 * 1000, - max: 1, - keyGenerator: request => { - const data = request.headers['x-nintendo-device-cert']; - return crypto.createHash('md5').update(data).digest('hex'); - } -}); \ No newline at end of file diff --git a/src/middleware/ratelimit.ts b/src/middleware/ratelimit.ts new file mode 100644 index 0000000..0cc8b38 --- /dev/null +++ b/src/middleware/ratelimit.ts @@ -0,0 +1,18 @@ +import crypto from 'node:crypto'; +import express from 'express'; +import ratelimit from 'express-rate-limit'; +import { getValueFromHeaders } from '@/util'; + +export default ratelimit({ + windowMs: 60 * 1000, + max: 1, + keyGenerator: (request: express.Request) => { + let data: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-device-cert'); + + if (!data) { + data = request.ip; + } + + return crypto.createHash('md5').update(data).digest('hex'); + } +}); \ No newline at end of file diff --git a/src/middleware/xml-parser.js b/src/middleware/xml-parser.js deleted file mode 100644 index 323e045..0000000 --- a/src/middleware/xml-parser.js +++ /dev/null @@ -1,53 +0,0 @@ -const xmlbuilder = require('xmlbuilder'); -const { document: xmlParser } = require('xmlbuilder2'); - -function XMLMiddleware(request, response, next) { - if (request.method == 'POST' || request.method == 'PUT') { - const headers = request.headers; - let body = ''; - - if ( - !headers['content-type'] || - !headers['content-type'].toLowerCase().includes('xml') - ) { - return next(); - } - - if ( - !headers['content-length'] || - parseInt(headers['content-length']) === 0 - ) { - return next(); - } - - request.setEncoding('utf-8'); - request.on('data', (chunk) => { - body += chunk; - }); - - request.on('end', () => { - try { - request.body = xmlParser(body); - request.body = request.body.toObject(); - } catch (error) { - response.status(401); - - // TODO: This is not a real error code, check to see if better one exists - return response.send(xmlbuilder.create({ - errors: { - error: { - code: '0004', - message: 'XML parse error' - } - } - }).end()); - } - - next(); - }); - } else { - next(); - } -} - -module.exports = XMLMiddleware; \ No newline at end of file diff --git a/src/middleware/xml-parser.ts b/src/middleware/xml-parser.ts new file mode 100644 index 0000000..4f639d5 --- /dev/null +++ b/src/middleware/xml-parser.ts @@ -0,0 +1,57 @@ +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import { document as xmlParser } from 'xmlbuilder2'; +import { getValueFromHeaders, mapToObject } from '@/util'; + +function XMLMiddleware(request: express.Request, response: express.Response, next: express.NextFunction): void { + if (request.method == 'POST' || request.method == 'PUT') { + const contentType: string | undefined = getValueFromHeaders(request.headers, 'content-type'); + const contentLength: string | undefined = getValueFromHeaders(request.headers, 'content-length'); + let body: string = ''; + + if ( + !contentType || + !contentType.toLowerCase().includes('xml') + ) { + return next(); + } + + if ( + !contentLength || + parseInt(contentLength) === 0 + ) { + return next(); + } + + request.setEncoding('utf-8'); + request.on('data', (chunk: string) => { + body += chunk; + }); + + request.on('end', () => { + try { + request.body = xmlParser(body); + request.body = request.body.toObject(); + request.body = mapToObject(request.body); + } catch (error) { + response.status(401); + + // TODO: This is not a real error code, check to see if better one exists + return response.send(xmlbuilder.create({ + errors: { + error: { + code: '0004', + message: 'XML parse error' + } + } + }).end()); + } + + next(); + }); + } else { + next(); + } +} + +export default XMLMiddleware; \ No newline at end of file diff --git a/src/mii.js b/src/mii.js deleted file mode 100644 index 437ae16..0000000 --- a/src/mii.js +++ /dev/null @@ -1,214 +0,0 @@ -const KaitaiStream = require('kaitai-struct/KaitaiStream'); - -class Mii extends KaitaiStream { - constructor(arrayBuffer, byteOffset) { - super(arrayBuffer, byteOffset); - - this.decode(); - } - - decode() { - // Decode raw data - // A lot of this goes unused - this.unknown1 = this.readU1(); - this.characterSet = this.readBitsIntBe(2); - this.regionLock = this.readBitsIntBe(2); - this.profanityFlag = this.readBitsIntBe(1) !== 0; - this.copying = this.readBitsIntBe(1) !== 0; - this.unknown2 = this.readBitsIntBe(2); - this.slotIndex = this.readBitsIntBe(4); - this.pageIndex = this.readBitsIntBe(4); - this.version = this.readBitsIntBe(4); - this.unknown3 = this.readBitsIntBe(4); - this.systemId = Array(8).fill().map(() => this.readU1()); - this.avatarId = Array(4).fill().map(() => this.readU1()); - this.clientId = Array(6).fill().map(() => this.readU1()); - this.padding = this.readU2le(); - this.miiMetaData = this.readU2le(); - this.miiName = Buffer.from(this.readBytes(20)).toString('utf16le'); - this.height = this.readU1(); - this.build = this.readU1(); - this.faceColor = this.readBitsIntBe(3); - this.faceType = this.readBitsIntBe(4); - this.mingle = this.readBitsIntBe(1) !== 0; - this.faceMakeup = this.readBitsIntBe(4); - this.faceWrinkles = this.readBitsIntBe(4); - this.alignToByte(); - this.hairType = this.readU1(); - this.unknown5 = this.readBitsIntBe(4); - this.hairFlip = this.readBitsIntBe(1) !== 0; - this.hairColor = this.readBitsIntBe(3); - this.alignToByte(); - this.eyeData = this.readU4le(); - this.eyebrowData = this.readU4le(); - this.noseData = this.readU2le(); - this.mouthData = this.readU2le(); - this.mouthData2 = this.readU2le(); - this.facialHairData = this.readU2le(); - this.glassesData = this.readU2le(); - this.moleData = this.readU2le(); - this.creatorName = Buffer.from(this.readBytes(20)).toString('utf16le'); - this.padding2 = this.readU2le(); - this.checksum = this.readU2le(); - - // Carve out more specific data from the above values - // TODO: read these bits directly instead of getting them later - - this.gender = (this.miiMetaData & 1); - this.birthMonth = ((this.miiMetaData >> 1) & 15); - this.birthDay = ((this.miiMetaData >> 5) & 31); - this.favoriteColor = ((this.miiMetaData >> 10) & 15); - this.favorite = ((this.miiMetaData >> 14) & 1); - - this.eyeType = (this.eyeData & 63); - this.eyeColor = ((this.eyeData >> 6) & 7); - this.eyeSize = ((this.eyeData >> 9) & 7); - this.eyeStretch = ((this.eyeData >> 13) & 7); - this.eyeRotation = ((this.eyeData >> 16) & 31); - this.eyeHorizontal = ((this.eyeData >> 21) & 15); - this.eyeVertical = ((this.eyeData >> 25) & 31); - - this.eyebrowType = (this.eyebrowData & 31); - this.eyebrowColor = ((this.eyebrowData >> 5) & 7); - this.eyebrowSize = ((this.eyebrowData >> 8) & 15); - this.eyebrowStretch = ((this.eyebrowData >> 12) & 7); - this.eyebrowRotation = ((this.eyebrowData >> 16) & 15); - this.eyebrowHorizontal = ((this.eyebrowData >> 21) & 15); - this.eyebrowVertical = ((this.eyebrowData >> 25) & 31); - - this.noseType = (this.noseData & 31); - this.noseSize = ((this.noseData >> 5) & 15); - this.noseVertical = ((this.noseData >> 9) & 31); - - - this.mouthType = (this.mouthData & 63); - this.mouthColor = ((this.mouthData >> 6) & 7); - this.mouthSize = ((this.mouthData >> 9) & 15); - this.mouthStretch = ((this.mouthData >> 13) & 7); - - this.mouthVertical = (this.mouthData2 & 31); - this.facialHairMustache = ((this.mouthData2 >> 5) & 7); - - this.facialHairType = (this.facialHairData & 7); - this.facialHairColor = ((this.facialHairData >> 3) & 7); - this.facialHairSize = ((this.facialHairData >> 6) & 15); - this.facialHairVertical = ((this.facialHairData >> 10) & 31); - - this.glassesType = (this.glassesData & 15); - this.glassesColor = (this.glassesData >> 4) & 7; - this.glassesSize = (this.glassesData >> 7) & 15; - this.glassesVertical = (this.glassesData >> 11) & 15; - - this.moleEnable = (this.moleData >> 15); - this.moleSize = ((this.moleData >> 1) & 15); - this.moleHorizontal = ((this.moleData >> 5) & 31); - this.moleVertical = ((this.moleData >> 10) & 31); - } - - toStudioMii() { - /* - Can also disable randomization with: - - let miiStudioData = Buffer.alloc(0x2F); - let next = 256; - - and removing "randomizer" and the "miiStudioData.writeUInt8(randomizer);" call - */ - const miiStudioData = Buffer.alloc(0x2F); - const randomizer = Math.floor(256 * Math.random()); - let next = randomizer; - let pos = 1; - - function encodeMiiPart(partValue) { - const encoded = (7 + (partValue ^ next)) % 256; - next = encoded; - - miiStudioData.writeUInt8(encoded, pos); - pos++; - } - - miiStudioData.writeUInt8(randomizer); - - if (this.facialHairColor === 0) { - encodeMiiPart(8); - } else { - encodeMiiPart(this.facialHairColor); - } - - encodeMiiPart(this.facialHairType); - encodeMiiPart(this.build); - encodeMiiPart(this.eyeStretch); - encodeMiiPart(this.eyeColor + 8); - encodeMiiPart(this.eyeRotation); - encodeMiiPart(this.eyeSize); - encodeMiiPart(this.eyeType); - encodeMiiPart(this.eyeHorizontal); - encodeMiiPart(this.eyeVertical); - encodeMiiPart(this.eyebrowStretch); - - if (this.eyebrowColor === 0) { - encodeMiiPart(8); - } else { - encodeMiiPart(this.eyebrowColor); - } - - encodeMiiPart(this.eyebrowRotation); - encodeMiiPart(this.eyebrowSize); - encodeMiiPart(this.eyebrowType); - encodeMiiPart(this.eyebrowHorizontal); - encodeMiiPart(this.eyebrowVertical); - encodeMiiPart(this.faceColor); - encodeMiiPart(this.faceMakeup); - encodeMiiPart(this.faceType); - encodeMiiPart(this.faceWrinkles); - encodeMiiPart(this.favoriteColor); - encodeMiiPart(this.gender); - - if (this.glassesColor == 0) { - encodeMiiPart(8); - } else if (this.glassesColor < 6) { - encodeMiiPart(this.glassesColor + 13); - } else { - encodeMiiPart(0); - } - - encodeMiiPart(this.glassesSize); - encodeMiiPart(this.glassesType); - encodeMiiPart(this.glassesVertical); - - if (this.hairColor == 0) { - encodeMiiPart(8); - } else { - encodeMiiPart(this.hairColor); - } - - encodeMiiPart(this.hairFlip ? 1 : 0); - encodeMiiPart(this.hairType); - encodeMiiPart(this.height); - encodeMiiPart(this.moleSize); - encodeMiiPart(this.moleEnable); - encodeMiiPart(this.moleHorizontal); - encodeMiiPart(this.moleVertical); - encodeMiiPart(this.mouthStretch); - - if (this.mouthColor < 4) { - encodeMiiPart(this.mouthColor + 19); - } else { - encodeMiiPart(0); - } - - encodeMiiPart(this.mouthSize); - encodeMiiPart(this.mouthType); - encodeMiiPart(this.mouthVertical); - encodeMiiPart(this.facialHairSize); - encodeMiiPart(this.facialHairMustache); - encodeMiiPart(this.facialHairVertical); - encodeMiiPart(this.noseSize); - encodeMiiPart(this.noseType); - encodeMiiPart(this.noseVertical); - - return miiStudioData; - } -} - -module.exports = Mii; \ No newline at end of file diff --git a/src/models/device.js b/src/models/device.ts similarity index 56% rename from src/models/device.js rename to src/models/device.ts index 00d667b..1866e7f 100644 --- a/src/models/device.js +++ b/src/models/device.ts @@ -1,14 +1,14 @@ -const { Schema, model } = require('mongoose'); +import { Schema, model } from 'mongoose'; +import { IDeviceAttribute, IDeviceAttributeMethods, DeviceAttributeModel } from '@/types/mongoose/device-attribute'; +import { IDevice, IDeviceMethods, DeviceModel } from '@/types/mongoose/device'; -const DeviceAttributeSchema = new Schema({ +const DeviceAttributeSchema = new Schema({ created_date: String, name: String, - value: String, + value: String }); -const DeviceAttribute = model('DeviceAttribute', DeviceAttributeSchema); - -const DeviceSchema = new Schema({ +export const DeviceSchema = new Schema({ is_emulator: { type: Boolean, default: false @@ -22,7 +22,7 @@ const DeviceSchema = new Schema({ 'ftr', // Nintendo 2DS 'ktr', // New Nintendo 3DS 'red', // New Nintendo 3DS XL - 'jan' // New Nintendo 2DS XL + 'jan' // New Nintendo 2DS XL ] }, device_id: Number, @@ -48,11 +48,4 @@ const DeviceSchema = new Schema({ } }); -const Device = model('Device', DeviceSchema); - -module.exports = { - DeviceSchema, - Device, - DeviceAttributeSchema, - DeviceAttribute -}; \ No newline at end of file +export const Device: DeviceModel = model('Device', DeviceSchema); \ No newline at end of file diff --git a/src/models/nex-account.js b/src/models/nex-account.ts similarity index 53% rename from src/models/nex-account.js rename to src/models/nex-account.ts index fbffd69..c302c4b 100644 --- a/src/models/nex-account.js +++ b/src/models/nex-account.ts @@ -1,7 +1,8 @@ -const { Schema, model } = require('mongoose'); -const uniqueValidator = require('mongoose-unique-validator'); +import { Schema, model } from 'mongoose'; +import uniqueValidator from 'mongoose-unique-validator'; +import { HydratedNEXAccountDocument, INEXAccount, INEXAccountMethods, NEXAccountModel } from '@/types/mongoose/nex-account'; -const NEXAccountSchema = new Schema({ +const NEXAccountSchema = new Schema({ device_type: { type: String, enum: [ @@ -23,7 +24,7 @@ const NEXAccountSchema = new Schema({ server_access_level: { type: String, default: 'prod' // everyone is in production by default - }, + } }); NEXAccountSchema.plugin(uniqueValidator, { message: '{PATH} already in use.' }); @@ -36,39 +37,36 @@ NEXAccountSchema.plugin(uniqueValidator, { message: '{PATH} already in use.' }); https://account.nintendo.net/v1/api/admin/mapped_ids?input_type=pid&output_type=user_id&input=1799999999 returns `prodtest1` and the next few accounts counting down seem to be admin, service and internal test accounts */ -NEXAccountSchema.methods.generatePID = async function () { - const min = 1000000000; // The console (WiiU) seems to not accept PIDs smaller than this - const max = 1799999999; +NEXAccountSchema.method('generatePID', async function generatePID(): Promise { + const min: number = 1000000000; // The console (WiiU) seems to not accept PIDs smaller than this + const max: number = 1799999999; - let pid = Math.floor(Math.random() * (max - min + 1) + min); + const pid: number = Math.floor(Math.random() * (max - min + 1) + min); - const inuse = await NEXAccount.findOne({ pid }); + const inuse: HydratedNEXAccountDocument | null = await NEXAccount.findOne({ pid }); - pid = (inuse ? await NEXAccount.generatePID() : pid); + if (inuse) { + await this.generatePID(); + } else { + this.pid = pid; + } +}); - this.set('pid', pid); -}; - -NEXAccountSchema.methods.generatePassword = function () { - function character() { - const offset = Math.floor(Math.random() * 62); +NEXAccountSchema.method('generatePassword', function generatePassword(): void { + function character(): string | number { + const offset: number = Math.floor(Math.random() * 62); if (offset < 10) return offset; if (offset < 36) return String.fromCharCode(offset + 55); return String.fromCharCode(offset + 61); } - const output = []; + const output: string[] = []; while (output.length < 16) { - output.push(character()); + output.push(String(character())); } - this.set('password', output.join('')); -}; + this.password = output.join(''); +}); -const NEXAccount = model('NEXAccount', NEXAccountSchema); - -module.exports = { - NEXAccountSchema, - NEXAccount, -}; \ No newline at end of file +export const NEXAccount: NEXAccountModel = model('NEXAccount', NEXAccountSchema); \ No newline at end of file diff --git a/src/models/pnid.js b/src/models/pnid.js deleted file mode 100644 index 5cf0f99..0000000 --- a/src/models/pnid.js +++ /dev/null @@ -1,239 +0,0 @@ -const { Schema, model } = require('mongoose'); -const uniqueValidator = require('mongoose-unique-validator'); -const bcrypt = require('bcrypt'); -const crypto = require('crypto'); -const imagePixels = require('image-pixels'); -const TGA = require('tga'); -const got = require('got'); -const util = require('../util'); -const { DeviceSchema } = require('./device'); -const Mii = require('mii-js'); - -const PNIDSchema = new Schema({ - access_level: { - type: Number, - default: 0 // 0: standard, 1: tester, 2: mod?, 3: dev - }, - server_access_level: { - type: String, - default: 'prod' // everyone is in production by default - }, - pid: { - type: Number, - unique: true - }, - creation_date: String, - updated: String, - username: { - type: String, - unique: true, - minlength: 6, - maxlength: 16 - }, - usernameLower: { - type: String, - unique: true - }, - password: String, - birthdate: String, - gender: String, - country: String, - language: String, - email: { - address: String, - primary: Boolean, - parent: Boolean, - reachable: Boolean, - validated: Boolean, - validated_date: String, - id: { - type: Number, - unique: true - } - }, - region: Number, - timezone: { - name: String, - offset: Number - }, - mii: { - name: String, - primary: Boolean, - data: String, - id: { - type: Number, - unique: true - }, - hash: { - type: String, - unique: true - }, - image_url: String, - image_id: { - type: Number, - unique: true - }, - }, - flags: { - active: Boolean, - marketing: Boolean, - off_device: Boolean - }, - devices: [DeviceSchema], - identification: { // user identification tokens - email_code: { - type: String, - unique: true - }, - email_token: { - type: String, - unique: true - }, - access_token: { - value: String, - ttl: Number - }, - refresh_token: { - value: String, - ttl: Number - } - }, - connections: { - discord: { - id: String - } - } -}); - -PNIDSchema.plugin(uniqueValidator, {message: '{PATH} already in use.'}); - -/* - According to http://pf2m.com/tools/rank.php Nintendo PID's start at 1,800,000,000 and count down with each account - This means the max PID is 1799999999 and hard-limits the number of potential accounts to 1,800,000,000 - The author of that site does not give any information on how they found this out, but it does seem to hold true - https://account.nintendo.net/v1/api/admin/mapped_ids?input_type=pid&output_type=user_id&input=1800000000 returns nothing - https://account.nintendo.net/v1/api/admin/mapped_ids?input_type=pid&output_type=user_id&input=1799999999 returns `prodtest1` - and the next few accounts counting down seem to be admin, service and internal test accounts -*/ -PNIDSchema.methods.generatePID = async function() { - const min = 1000000000; // The console (WiiU) seems to not accept PIDs smaller than this - const max = 1799999999; - - let pid = Math.floor(Math.random() * (max - min + 1) + min); - - const inuse = await PNID.findOne({ - pid - }); - - pid = (inuse ? await PNID.generatePID() : pid); - - this.set('pid', pid); -}; - -PNIDSchema.methods.generateEmailValidationCode = async function() { - // WiiU passes the PID along with the email code - // Does not actually need to be unique to all users - const code = Math.random().toFixed(6).split('.')[1]; // Dirty one-liner to generate numbers of 6 length and padded 0 - - this.set('identification.email_code', code); -}; - -PNIDSchema.methods.generateEmailValidationToken = async function() { - let token = crypto.randomBytes(32).toString('hex'); - - const inuse = await PNID.findOne({ - 'identification.email_token': token - }); - - token = (inuse ? await PNID.generateEmailValidationToken() : token); - - this.set('identification.email_token', token); -}; - -PNIDSchema.methods.getDevice = async function(document) { - const devices = this.get('devices'); - - return devices.find(device => { - return ( - (device.device_id === document.device_id) && - (device.device_type === document.device_type) && - (device.serial === document.serial) - ); - }); -}; - -PNIDSchema.methods.addDevice = async function(device) { - this.devices.push(device); - - await this.save(); -}; - -PNIDSchema.methods.removeDevice = async function(device) { - this.devices = this.devices.filter(({ _id }) => _id !== device._id); - - await this.save(); -}; - -PNIDSchema.methods.updateMii = async function({name, primary, data}) { - this.set('mii.name', name); - this.set('mii.primary', primary === 'Y'); - this.set('mii.data', data); - this.set('mii.hash', crypto.randomBytes(7).toString('hex')); - this.set('mii.id', crypto.randomBytes(4).readUInt32LE()); - this.set('mii.image_id', crypto.randomBytes(4).readUInt32LE()); - - await this.generateMiiImages(); - - await this.save(); -}; - -PNIDSchema.methods.generateMiiImages = async function() { - const miiData = this.get('mii.data'); - const mii = new Mii(Buffer.from(miiData, 'base64')); - const miiStudioUrl = mii.studioUrl({ - type: 'face', - width: '128', - instanceCount: '1', - }); - const miiStudioNormalFaceImageData = await got(miiStudioUrl).buffer(); - const pngData = await imagePixels(miiStudioNormalFaceImageData); - const tga = TGA.createTgaBuffer(pngData.width, pngData.height, pngData.data); - - const userMiiKey = `mii/${this.get('pid')}`; - - await util.uploadCDNAsset('pn-cdn', `${userMiiKey}/standard.tga`, tga, 'public-read'); - await util.uploadCDNAsset('pn-cdn', `${userMiiKey}/normal_face.png`, miiStudioNormalFaceImageData, 'public-read'); - - const expressions = ['frustrated', 'smile_open_mouth', 'wink_left', 'sorrow', 'surprise_open_mouth']; - for (const expression of expressions) { - const miiStudioExpressionUrl = mii.studioUrl({ - type: 'face', - expression: expression, - width: '128', - instanceCount: '1', - }); - const miiStudioExpressionImageData = await got(miiStudioExpressionUrl).buffer(); - await util.uploadCDNAsset('pn-cdn', `${userMiiKey}/${expression}.png`, miiStudioExpressionImageData, 'public-read'); - } - - const miiStudioBodyUrl = mii.studioUrl({ - type: 'all_body', - width: '270', - instanceCount: '1', - }); - const miiStudioBodyImageData = await got(miiStudioBodyUrl).buffer(); - await util.uploadCDNAsset('pn-cdn', `${userMiiKey}/body.png`, miiStudioBodyImageData, 'public-read'); -}; - -PNIDSchema.methods.getServerMode = function () { - const serverMode = this.get('server_mode') || 'prod'; - - return serverMode; -}; - -const PNID = model('PNID', PNIDSchema); - -module.exports = { - PNIDSchema, - PNID -}; \ No newline at end of file diff --git a/src/models/pnid.ts b/src/models/pnid.ts new file mode 100644 index 0000000..74613f6 --- /dev/null +++ b/src/models/pnid.ts @@ -0,0 +1,212 @@ +import crypto from 'node:crypto'; +import { Schema, model } from 'mongoose'; +import uniqueValidator from 'mongoose-unique-validator'; +import imagePixels from 'image-pixels'; +import TGA from 'tga'; +import got from 'got'; +import Mii from 'mii-js'; +import { DeviceSchema } from '@/models/device'; +import { uploadCDNAsset } from '@/util'; +import { HydratedPNIDDocument, IPNID, IPNIDMethods, PNIDModel } from '@/types/mongoose/pnid'; + +const PNIDSchema = new Schema({ + access_level: { + type: Number, + default: 0 // 0: standard, 1: tester, 2: mod?, 3: dev + }, + server_access_level: { + type: String, + default: 'prod' // everyone is in production by default + }, + pid: { + type: Number, + unique: true + }, + creation_date: String, + updated: String, + username: { + type: String, + unique: true, + minlength: 6, + maxlength: 16 + }, + usernameLower: { + type: String, + unique: true + }, + password: String, + birthdate: String, + gender: String, + country: String, + language: String, + email: { + address: String, + primary: Boolean, + parent: Boolean, + reachable: Boolean, + validated: Boolean, + validated_date: String, + id: { + type: Number, + unique: true + } + }, + region: Number, + timezone: { + name: String, + offset: Number + }, + mii: { + name: String, + primary: Boolean, + data: String, + id: { + type: Number, + unique: true + }, + hash: { + type: String, + unique: true + }, + image_url: String, + image_id: { + type: Number, + unique: true + }, + }, + flags: { + active: Boolean, + marketing: Boolean, + off_device: Boolean + }, + devices: [DeviceSchema], + identification: { // user identification tokens + email_code: { + type: String, + unique: true + }, + email_token: { + type: String, + unique: true + }, + access_token: { + value: String, + ttl: Number + }, + refresh_token: { + value: String, + ttl: Number + } + }, + connections: { + discord: { + id: String + } + } +}, { id: false }); + +PNIDSchema.plugin(uniqueValidator, {message: '{PATH} already in use.'}); + +/* + According to http://pf2m.com/tools/rank.php Nintendo PID's start at 1,800,000,000 and count down with each account + This means the max PID is 1799999999 and hard-limits the number of potential accounts to 1,800,000,000 + The author of that site does not give any information on how they found this out, but it does seem to hold true + https://account.nintendo.net/v1/api/admin/mapped_ids?input_type=pid&output_type=user_id&input=1800000000 returns nothing + https://account.nintendo.net/v1/api/admin/mapped_ids?input_type=pid&output_type=user_id&input=1799999999 returns `prodtest1` + and the next few accounts counting down seem to be admin, service and internal test accounts +*/ +PNIDSchema.method('generatePID', async function generatePID(): Promise { + const min: number = 1000000000; // The console (WiiU) seems to not accept PIDs smaller than this + const max: number = 1799999999; + + const pid: number = Math.floor(Math.random() * (max - min + 1) + min); + + const inuse: HydratedPNIDDocument | null = await PNID.findOne({ + pid + }); + + if (inuse) { + await this.generatePID(); + } else { + this.pid = pid; + } +}); + +PNIDSchema.method('generateEmailValidationCode', async function generateEmailValidationCode(): Promise { + // WiiU passes the PID along with the email code + // Does not actually need to be unique to all users + const code: string = Math.random().toFixed(6).split('.')[1]; // Dirty one-liner to generate numbers of 6 length and padded 0 + + this.identification.email_code = code; +}); + +PNIDSchema.method('generateEmailValidationToken', async function generateEmailValidationToken(): Promise { + const token: string = crypto.randomBytes(32).toString('hex'); + + const inuse: HydratedPNIDDocument | null = await PNID.findOne({ + 'identification.email_token': token + }); + + if (inuse) { + await this.generateEmailValidationToken(); + } else { + this.identification.email_token = token; + } +}); + +PNIDSchema.method('updateMii', async function updateMii({name, primary, data}): Promise { + this.mii.name = name; + this.mii.primary = primary === 'Y'; + this.mii.data = data; + this.mii.hash = crypto.randomBytes(7).toString('hex'); + this.mii.id = crypto.randomBytes(4).readUInt32LE(); + this.mii.image_id = crypto.randomBytes(4).readUInt32LE(); + + await this.generateMiiImages(); + + await this.save(); +}); + +PNIDSchema.method('generateMiiImages', async function generateMiiImages(): Promise { + const miiData: string = this.get('mii.data'); + const mii: Mii = new Mii(Buffer.from(miiData, 'base64')); + const miiStudioUrl: string = mii.studioUrl({ + type: 'face', + width: 128, + instanceCount: 1, + }); + const miiStudioNormalFaceImageData: Buffer = await got(miiStudioUrl).buffer(); + const pngData: ImageData = await imagePixels(miiStudioNormalFaceImageData); + const tga: Buffer = TGA.createTgaBuffer(pngData.width, pngData.height, Uint8Array.from(pngData.data), false); + + const userMiiKey: string = `mii/${this.get('pid')}`; + + await uploadCDNAsset('pn-cdn', `${userMiiKey}/standard.tga`, tga, 'public-read'); + await uploadCDNAsset('pn-cdn', `${userMiiKey}/normal_face.png`, miiStudioNormalFaceImageData, 'public-read'); + + const expressions: string[] = ['frustrated', 'smile_open_mouth', 'wink_left', 'sorrow', 'surprise_open_mouth']; + for (const expression of expressions) { + const miiStudioExpressionUrl: string = mii.studioUrl({ + type: 'face', + expression: expression, + width: 128, + instanceCount: 1, + }); + const miiStudioExpressionImageData: Buffer = await got(miiStudioExpressionUrl).buffer(); + await uploadCDNAsset('pn-cdn', `${userMiiKey}/${expression}.png`, miiStudioExpressionImageData, 'public-read'); + } + + const miiStudioBodyUrl: string = mii.studioUrl({ + type: 'all_body', + width: 270, + instanceCount: 1, + }); + const miiStudioBodyImageData: Buffer = await got(miiStudioBodyUrl).buffer(); + await uploadCDNAsset('pn-cdn', `${userMiiKey}/body.png`, miiStudioBodyImageData, 'public-read'); +}); + +PNIDSchema.method('getServerMode', function getServerMode(): string { + return this.get('server_mode') || 'prod'; +}); + +export const PNID: PNIDModel = model('PNID', PNIDSchema); \ No newline at end of file diff --git a/src/models/server.js b/src/models/server.js deleted file mode 100644 index 8b70a7a..0000000 --- a/src/models/server.js +++ /dev/null @@ -1,23 +0,0 @@ -const { Schema, model } = require('mongoose'); -const uniqueValidator = require('mongoose-unique-validator'); - -const ServerSchema = new Schema({ - ip: String, // Example: 1.1.1.1 - port: Number, // Example: 60000 - service_name: String, // Example: friends - service_type: String, // Example: nex - game_server_id: String, // Example: 00003200 - title_ids: [String], // Example: ["000500001018DB00", "000500001018DC00", "000500001018DD00"] - access_mode: String, // Example: prod - maintenance_mode: Boolean, // Example: false - device: Number, // Example: 1 (WiiU) -}); - -ServerSchema.plugin(uniqueValidator, { message: '{PATH} already in use.' }); - -const Server = model('Server', ServerSchema); - -module.exports = { - ServerSchema, - Server, -}; \ No newline at end of file diff --git a/src/models/server.ts b/src/models/server.ts new file mode 100644 index 0000000..c953323 --- /dev/null +++ b/src/models/server.ts @@ -0,0 +1,20 @@ +import { Schema, model } from 'mongoose'; +import uniqueValidator from 'mongoose-unique-validator'; +import { IServer, IServerMethods, ServerModel } from '@/types/mongoose/server'; + +const ServerSchema = new Schema({ + ip: String, + port: Number, + service_name: String, + service_type: String, + game_server_id: String, + title_ids: [String], + access_mode: String, + maintenance_mode: Boolean, + device: Number, + aes_key: String +}); + +ServerSchema.plugin(uniqueValidator, { message: '{PATH} already in use.' }); + +export const Server: ServerModel = model('Server', ServerSchema); \ No newline at end of file diff --git a/src/nintendo-certificate.js b/src/nintendo-certificate.ts similarity index 74% rename from src/nintendo-certificate.js rename to src/nintendo-certificate.ts index 6df33ee..a371c8f 100644 --- a/src/nintendo-certificate.js +++ b/src/nintendo-certificate.ts @@ -1,7 +1,6 @@ -// Parse Nintendo certificates - -const crypto = require('crypto'); -const NodeRSA = require('node-rsa'); +import crypto from 'node:crypto'; +import NodeRSA from 'node-rsa'; +import { SignatureSize } from '@/types/common/signature-size'; const WIIU_DEVICE_PUB_PEM = `-----BEGIN PUBLIC KEY----- MFIwEAYHKoZIzj0CAQYFK4EEABsDPgAEAP1WBBgs8XUJIQDDCK5IOZEbb5+h1TqV @@ -44,44 +43,57 @@ const CTR_LFCS_B_PUB = Buffer.from([ // Signature options const SIGNATURE_SIZES = { - RSA_4096_SHA1: { + RSA_4096_SHA1: { SIZE: 0x200, PADDING_SIZE: 0x3C }, - RSA_2048_SHA1: { + RSA_2048_SHA1: { SIZE: 0x100, PADDING_SIZE: 0x3C }, - ELLIPTIC_CURVE_SHA1: { + ELLIPTIC_CURVE_SHA1: { SIZE: 0x3C, PADDING_SIZE: 0x40 }, - RSA_4096_SHA256: { + RSA_4096_SHA256: { SIZE: 0x200, PADDING_SIZE: 0x3C }, - RSA_2048_SHA256: { + RSA_2048_SHA256: { SIZE: 0x100, PADDING_SIZE: 0x3C }, - ECDSA_SHA256: { + ECDSA_SHA256: { SIZE: 0x3C, PADDING_SIZE: 0x40 } -}; +} as const; class NintendoCertificate { - constructor(certificate) { - this._certificate = null; - this._certificateBody = null; - this.signatureType = null; - this.signature = null; - this.issuer = null; - this.keyType = null; - this.certificateName = null; - this.ngKeyId = null; - this.publicKey = null; - this.valid = null; + _certificate: Buffer; + _certificateBody: Buffer; + signatureType: number; + signature: Buffer; + issuer: string; + keyType: number; + certificateName: string; + ngKeyId: number; + publicKey: Buffer; + valid: boolean; + publicKeyData: Buffer; + + constructor(certificate: string | Buffer) { + this._certificate = Buffer.alloc(0); + this._certificateBody = Buffer.alloc(0); + this.signatureType = 0; + this.signature = Buffer.alloc(0); + this.issuer = ''; + this.keyType = 0; + this.certificateName = ''; + this.ngKeyId = 0; + this.publicKey = Buffer.alloc(0); + this.valid = false; + this.publicKeyData = Buffer.alloc(0); if (certificate) { if (certificate instanceof Buffer) { @@ -91,11 +103,10 @@ class NintendoCertificate { } this._parseCertificateData(); - this._verifySignature(); } } - _parseCertificateData() { + _parseCertificateData(): void { if (this._certificate.length === 0x110) { // Assume fcdcert (3DS LFCS) this.signature = this._certificate.subarray(0x0, 0x100); @@ -106,7 +117,7 @@ class NintendoCertificate { // Assume regular certificate this.signatureType = this._certificate.readUInt32BE(0x00); - const signatureTypeSizes = this._signatureTypeSizes(this.signatureType); + const signatureTypeSizes: SignatureSize = this._signatureTypeSizes(this.signatureType); this._certificateBody = this._certificate.subarray(0x4 + signatureTypeSizes.SIZE + signatureTypeSizes.PADDING_SIZE); @@ -116,10 +127,12 @@ class NintendoCertificate { this.certificateName = this._certificate.subarray(0xC4, 0x104).toString().split('\0')[0]; this.ngKeyId = this._certificate.readUInt32BE(0x104); this.publicKeyData = this._certificate.subarray(0x108); + + this._verifySignature(); } } - _signatureTypeSizes(signatureType) { + _signatureTypeSizes(signatureType: number): SignatureSize { switch (signatureType) { case 0x10000: return SIGNATURE_SIZES.RSA_4096_SHA1; @@ -138,7 +151,7 @@ class NintendoCertificate { } } - _verifySignature() { + _verifySignature(): void { switch (this.keyType) { case 0x0: this._verifySignatureRSA4096(); @@ -157,8 +170,8 @@ class NintendoCertificate { } } - _verifySignatureRSA4096() { - const publicKey = new NodeRSA(); + _verifySignatureRSA4096(): void { + const publicKey: NodeRSA = new NodeRSA(); publicKey.importKey({ n: this.publicKeyData.subarray(0x0, 0x200), @@ -168,8 +181,8 @@ class NintendoCertificate { this.valid = publicKey.verify(this._certificateBody, this.signature); } - _verifySignatureRSA2048() { - const publicKey = new NodeRSA(); + _verifySignatureRSA2048(): void { + const publicKey: NodeRSA = new NodeRSA(); publicKey.importKey({ n: this.publicKeyData.subarray(0x0, 0x100), @@ -183,18 +196,18 @@ class NintendoCertificate { // with Nodes native crypto module and getting the keys // from bytes to PEM! // https://github.com/Myriachan - _verifySignatureECDSA() { - const pem = this.issuer == 'Root-CA00000003-MS00000012' ? WIIU_DEVICE_PUB_PEM : CTR_DEVICE_PUB_PEM; - const key = crypto.createPublicKey({ - key: pem - }); - key.dsaEncoding = 'ieee-p1363'; + _verifySignatureECDSA(): void { + const pem: string = this.issuer == 'Root-CA00000003-MS00000012' ? WIIU_DEVICE_PUB_PEM : CTR_DEVICE_PUB_PEM; + const key: crypto.VerifyPublicKeyInput = { + key: pem, + dsaEncoding: 'ieee-p1363' as crypto.DSAEncoding + }; this.valid = crypto.verify('sha256', this._certificateBody, key, this.signature); } - _verifySignatureLFCS() { - const publicKey = new NodeRSA(); + _verifySignatureLFCS(): void { + const publicKey: NodeRSA = new NodeRSA(); publicKey.importKey({ n: CTR_LFCS_B_PUB, @@ -205,4 +218,4 @@ class NintendoCertificate { } } -module.exports = NintendoCertificate; \ No newline at end of file +export default NintendoCertificate; \ No newline at end of file diff --git a/src/server.js b/src/server.js deleted file mode 100644 index 9372a3a..0000000 --- a/src/server.js +++ /dev/null @@ -1,93 +0,0 @@ -process.title = 'Pretendo - Account'; - -const configManager = require('./config-manager'); - -configManager.configure(); - -const express = require('express'); -const morgan = require('morgan'); -const xmlparser = require('./middleware/xml-parser'); -const cache = require('./cache'); -const database = require('./database'); -const util = require('./util'); -const logger = require('../logger'); - -const { config } = configManager; - -const { http: { port } } = config; -const app = express(); - -const conntest = require('./services/conntest'); -const nnid = require('./services/nnid'); -const nasc = require('./services/nasc'); -const datastore = require('./services/datastore'); -const api = require('./services/api'); -const localcdn = require('./services/local-cdn'); -const assets = require('./services/assets'); - -// START APPLICATION - -// Create router -logger.info('Setting up Middleware'); -app.use(morgan('dev')); -app.use(express.json()); -app.use(express.urlencoded({ - extended: true -})); -app.use(xmlparser); - -// import the servers into one -app.use(conntest); -app.use(nnid); -app.use(nasc); -app.use(datastore); -app.use(api); -app.use(localcdn); -app.use(assets); - -// 404 handler -logger.info('Creating 404 status handler'); -app.use((request, response) => { - const fullUrl = util.fullUrl(request); - const deviceId = request.headers['X-Nintendo-Device-ID'] || 'Unknown'; - - logger.warn(`HTTP 404 at ${fullUrl} from ${deviceId}`); - - response.set('Content-Type', 'text/xml'); - response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', new Date().getTime()); - - response.status(404); - response.send('0008Not Found'); -}); - -// non-404 error handler -logger.info('Creating non-404 status handler'); -app.use((error, request, response) => { - const status = error.status || 500; - const fullUrl = util.fullUrl(request); - const deviceId = request.headers['X-Nintendo-Device-ID'] || 'Unknown'; - - logger.warn(`HTTP ${status} at ${fullUrl} from ${deviceId}: ${error.message}`); - - response.status(status); - response.json({ - app: 'api', - status, - error: error.message - }); -}); - -async function main() { - // Starts the server - logger.info('Starting server'); - - await database.connect(); - await cache.connect(); - - app.listen(port, () => { - logger.success(`Server started on port ${port}`); - }); -} - -main().catch(console.error); \ No newline at end of file diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..1d9eac4 --- /dev/null +++ b/src/server.ts @@ -0,0 +1,109 @@ +process.title = 'Pretendo - Account'; +process.on('uncaughtException', (err, origin) => { + console.log(err); + console.log(origin); +}); + +import express from 'express'; +import morgan from 'morgan'; +import xmlbuilder from 'xmlbuilder'; +import xmlparser from '@/middleware/xml-parser'; +import { connect as connectCache } from '@/cache'; +import { connect as connectDatabase } from '@/database'; +import { fullUrl, getValueFromHeaders } from '@/util'; +import { LOG_INFO, LOG_SUCCESS, LOG_WARN } from '@/logger'; + +import conntest from '@/services/conntest'; +import nnid from '@/services/nnid'; +import nasc from '@/services/nasc'; +import datastore from '@/services/datastore'; +import api from '@/services/api'; +import localcdn from '@/services/local-cdn'; +import assets from '@/services/assets'; + +import { config } from '@/config-manager'; + +const { http: { port } } = config; +const app = express(); + +// START APPLICATION + +// Create router +LOG_INFO('Setting up Middleware'); +app.use(morgan('dev')); +app.use(express.json()); +app.use(express.urlencoded({ + extended: true +})); +app.use(xmlparser); + +// import the servers into one +app.use(conntest); +app.use(nnid); +app.use(nasc); +app.use(datastore); +app.use(api); +app.use(localcdn); +app.use(assets); + +// 404 handler +LOG_INFO('Creating 404 status handler'); +app.use((request: express.Request, response: express.Response) => { + const url: string = fullUrl(request); + let deviceId: string | undefined = getValueFromHeaders(request.headers, 'X-Nintendo-Device-ID'); + + if (!deviceId) { + deviceId = 'Unknown'; + } + + LOG_WARN(`HTTP 404 at ${url} from ${deviceId}`); + + response.set('Content-Type', 'text/xml'); + response.set('Server', 'Nintendo 3DS (http)'); + response.set('X-Nintendo-Date', new Date().getTime().toString()); + + response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); +}); + +// non-404 error handler +LOG_INFO('Creating non-404 status handler'); +app.use((error: any, request: express.Request, response: express.Response, _next: express.NextFunction) => { + const status: number = error.status || 500; + const url: string = fullUrl(request); + let deviceId: string | undefined = getValueFromHeaders(request.headers, 'X-Nintendo-Device-ID'); + + if (!deviceId) { + deviceId = 'Unknown'; + } + + LOG_WARN(`HTTP ${status} at ${url} from ${deviceId}: ${error.message}`); + + response.status(status); + response.json({ + app: 'api', + status, + error: error.message + }); +}); + +async function main(): Promise { + // Starts the server + LOG_INFO('Starting server'); + + await connectDatabase(); + await connectCache(); + + app.listen(port, () => { + LOG_SUCCESS(`Server started on port ${port}`); + }); +} + +main().catch(console.error); \ No newline at end of file diff --git a/src/services/api/index.js b/src/services/api/index.js deleted file mode 100644 index 8db69f5..0000000 --- a/src/services/api/index.js +++ /dev/null @@ -1,36 +0,0 @@ -// handles "api.nintendo.cc" endpoints - -const express = require('express'); -const subdomain = require('express-subdomain'); -const cors = require('cors'); -const APIMiddleware = require('../../middleware/api'); -const logger = require('../../../logger'); -const routes = require('./routes'); - -// Router to handle the subdomain restriction -const api = express.Router(); - -logger.info('[USER API] Importing middleware'); -api.use(APIMiddleware); -api.use(cors()); -api.options('*', cors()); - -// Setup routes -logger.info('[USER API] Applying imported routes'); -api.use('/v1/connections', routes.V1.CONNECTIONS); -api.use('/v1/email', routes.V1.EMAIL); -api.use('/v1/forgot-password', routes.V1.FORGOT_PASSWORD); -api.use('/v1/login', routes.V1.LOGIN); -api.use('/v1/register', routes.V1.REGISTER); -api.use('/v1/reset-password', routes.V1.RESET_PASSWORD); -api.use('/v1/user', routes.V1.USER); - - -// Main router for endpoints -const router = express.Router(); - -// Create subdomains -logger.info('[USER API] Creating \'api\' subdomain'); -router.use(subdomain('api', api)); - -module.exports = router; \ No newline at end of file diff --git a/src/services/api/index.ts b/src/services/api/index.ts new file mode 100644 index 0000000..18b7cde --- /dev/null +++ b/src/services/api/index.ts @@ -0,0 +1,37 @@ +// handles "api.nintendo.cc" endpoints + +import express from 'express'; +import subdomain from 'express-subdomain'; +import cors from 'cors'; +import APIMiddleware from '@/middleware/api'; +import { LOG_INFO } from '@/logger'; + +import { V1 } from '@/services/api/routes'; + +// Router to handle the subdomain restriction +const api: express.Router = express.Router(); + +LOG_INFO('[USER API] Importing middleware'); +api.use(APIMiddleware); +api.use(cors()); +api.options('*', cors()); + +// Setup routes +LOG_INFO('[USER API] Applying imported routes'); +api.use('/v1/connections', V1.CONNECTIONS); +api.use('/v1/email', V1.EMAIL); +api.use('/v1/forgot-password', V1.FORGOT_PASSWORD); +api.use('/v1/login', V1.LOGIN); +api.use('/v1/register', V1.REGISTER); +api.use('/v1/reset-password', V1.RESET_PASSWORD); +api.use('/v1/user', V1.USER); + + +// Main router for endpoints +const router: express.Router = express.Router(); + +// Create subdomains +LOG_INFO('[USER API] Creating \'api\' subdomain'); +router.use(subdomain('api', api)); + +export default router; \ No newline at end of file diff --git a/src/services/api/routes/index.js b/src/services/api/routes/index.js deleted file mode 100644 index 217a96d..0000000 --- a/src/services/api/routes/index.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - V1: { - CONNECTIONS: require('./v1/connections'), - EMAIL: require('./v1/email'), - FORGOT_PASSWORD: require('./v1/forgotPassword'), - LOGIN: require('./v1/login'), - REGISTER: require('./v1/register'), - RESET_PASSWORD: require('./v1/resetPassword'), - USER: require('./v1/user'), - } -}; \ No newline at end of file diff --git a/src/services/api/routes/index.ts b/src/services/api/routes/index.ts new file mode 100644 index 0000000..6f25c45 --- /dev/null +++ b/src/services/api/routes/index.ts @@ -0,0 +1,17 @@ +import connections_v1 from '@/services/api/routes/v1/connections'; +import email_v1 from '@/services/api/routes/v1/email'; +import forgotPassword_v1 from '@/services/api/routes/v1/forgotPassword'; +import login_v1 from '@/services/api/routes/v1/login'; +import register_v1 from '@/services/api/routes/v1/register'; +import resetPassword_v1 from '@/services/api/routes/v1/resetPassword'; +import user_v1 from '@/services/api/routes/v1/user'; + +export const V1 = { + CONNECTIONS: connections_v1, + EMAIL: email_v1, + FORGOT_PASSWORD: forgotPassword_v1, + LOGIN: login_v1, + REGISTER: register_v1, + RESET_PASSWORD: resetPassword_v1, + USER: user_v1 +}; \ No newline at end of file diff --git a/src/services/api/routes/v1/connections.js b/src/services/api/routes/v1/connections.js deleted file mode 100644 index 76e41c7..0000000 --- a/src/services/api/routes/v1/connections.js +++ /dev/null @@ -1,77 +0,0 @@ -const router = require('express').Router(); -const database = require('../../../../database'); - -const VALID_CONNECTION_TYPES = [ - 'discord' -]; - -/** - * [POST] - * Implementation of for: https://api.pretendo.cc/v1/connections/add/TYPE - * Description: Adds an account connection to the users PNID - */ -router.post('/add/:type', async (request, response) => { - const { body, pnid } = request; - const { type } = request.params; - const { data } = body; - - if (!pnid) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing access token' - }); - } - - if (!data) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing connection data' - }); - } - - if (!VALID_CONNECTION_TYPES.includes(type)) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing connection type' - }); - } - - const result = await database.addUserConnection(pnid, data, type); - - response.status(result.status).json(result); -}); - -/** - * [DELETE] - * Implementation of for: https://api.pretendo.cc/v1/connections/remove/TYPE - * Description: Removes an account connection from the users PNID - */ -router.delete('/remove/:type', async (request, response) => { - const { pnid } = request; - const { type } = request.params; - - if (!pnid) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing access token' - }); - } - - if (!VALID_CONNECTION_TYPES.includes(type)) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing connection type' - }); - } - - const result = await database.removeUserConnection(pnid, type); - - response.status(result.status).json(result); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/api/routes/v1/connections.ts b/src/services/api/routes/v1/connections.ts new file mode 100644 index 0000000..5bf79b4 --- /dev/null +++ b/src/services/api/routes/v1/connections.ts @@ -0,0 +1,98 @@ +import express from 'express'; +import { addPNIDConnection, removePNIDConnection } from '@/database'; +import { ConnectionData } from '@/types/services/api/connection-data'; +import { ConnectionResponse } from '@/types/services/api/connection-response'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +const router: express.Router = express.Router(); + +const VALID_CONNECTION_TYPES: string[] = [ + 'discord' +]; + +/** + * [POST] + * Implementation of for: https://api.pretendo.cc/v1/connections/add/TYPE + * Description: Adds an account connection to the users PNID + */ +router.post('/add/:type', async (request: express.Request, response: express.Response) => { + const data: ConnectionData = request.body?.data; + const pnid: HydratedPNIDDocument | null = request.pnid; + const type: string = request.params.type; + + if (!pnid) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing access token' + }); + } + + if (!data) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing connection data' + }); + } + + if (!VALID_CONNECTION_TYPES.includes(type)) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing connection type' + }); + } + + let result: ConnectionResponse | undefined = await addPNIDConnection(pnid, data, type); + + if (!result) { + result = { + app: 'api', + status: 500, + error: 'Unknown server error' + }; + } + + response.status(result.status || 500).json(result); +}); + +/** + * [DELETE] + * Implementation of for: https://api.pretendo.cc/v1/connections/remove/TYPE + * Description: Removes an account connection from the users PNID + */ +router.delete('/remove/:type', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + const type: string = request.params.type; + + if (!pnid) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing access token' + }); + } + + if (!VALID_CONNECTION_TYPES.includes(type)) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing connection type' + }); + } + + let result: ConnectionResponse | undefined = await removePNIDConnection(pnid, type); + + if (!result) { + result = { + app: 'api', + status: 500, + error: 'Unknown server error' + }; + } + + response.status(result.status).json(result); +}); + +export default router; \ No newline at end of file diff --git a/src/services/api/routes/v1/email.js b/src/services/api/routes/v1/email.js deleted file mode 100644 index 561e651..0000000 --- a/src/services/api/routes/v1/email.js +++ /dev/null @@ -1,42 +0,0 @@ -const router = require('express').Router(); -const moment = require('moment'); -const { PNID } = require('../../../../models/pnid'); -const util = require('../../../../util'); - -router.get('/verify', async (request, response) => { - const { token } = request.query; - - if (!token || token.trim() == '') { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Missing email token' - }); - } - - const pnid = await PNID.findOne({ - 'identification.email_token': token - }); - - if (!pnid) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid email token' - }); - } - - const validatedDate = moment().format('YYYY-MM-DDTHH:MM:SS'); - - pnid.set('email.reachable', true); - pnid.set('email.validated', true); - pnid.set('email.validated_date', validatedDate); - - await pnid.save(); - - await util.sendEmailConfirmedEmail(pnid); - - response.status(200).send('Email validated. You may close this window'); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/api/routes/v1/email.ts b/src/services/api/routes/v1/email.ts new file mode 100644 index 0000000..7a2efed --- /dev/null +++ b/src/services/api/routes/v1/email.ts @@ -0,0 +1,45 @@ +import express from 'express'; +import moment from 'moment'; +import { PNID } from '@/models/pnid'; +import { getValueFromQueryString, sendEmailConfirmedEmail } from '@/util'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +const router: express.Router = express.Router(); + +router.get('/verify', async (request: express.Request, response: express.Response) => { + const token: string | undefined = getValueFromQueryString(request.query, 'token'); + + if (!token || token.trim() == '') { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Missing email token' + }); + } + + const pnid: HydratedPNIDDocument | null = await PNID.findOne({ + 'identification.email_token': token + }); + + if (!pnid) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid email token' + }); + } + + const validatedDate: string = moment().format('YYYY-MM-DDTHH:MM:SS'); + + pnid.email.reachable = true; + pnid.email.validated = true; + pnid.email.validated_date = validatedDate; + + await pnid.save(); + + await sendEmailConfirmedEmail(pnid); + + response.status(200).send('Email validated. You may close this window'); +}); + +export default router; \ No newline at end of file diff --git a/src/services/api/routes/v1/forgotPassword.js b/src/services/api/routes/v1/forgotPassword.js deleted file mode 100644 index 7dd4948..0000000 --- a/src/services/api/routes/v1/forgotPassword.js +++ /dev/null @@ -1,36 +0,0 @@ -const router = require('express').Router(); -const validator = require('validator'); -const database = require('../../../../database'); -const util = require('../../../../util'); - -router.post('/', async (request, response) => { - const { body } = request; - const { input } = body; - - if (!input || input.trim() === '') { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing input' - }); - } - - let pnid; - - if (validator.isEmail(input)) { - pnid = await database.getUserByEmailAddress(input); - } else { - pnid = await database.getUserByUsername(input); - } - - if (pnid) { - await util.sendForgotPasswordEmail(pnid); - } - - response.json({ - app: 'api', - status: 200 - }); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/api/routes/v1/forgotPassword.ts b/src/services/api/routes/v1/forgotPassword.ts new file mode 100644 index 0000000..1cb3283 --- /dev/null +++ b/src/services/api/routes/v1/forgotPassword.ts @@ -0,0 +1,38 @@ +import express from 'express'; +import validator from 'validator'; +import { getPNIDByEmailAddress, getPNIDByUsername } from '@/database'; +import { sendForgotPasswordEmail } from '@/util'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +const router: express.Router = express.Router(); + +router.post('/', async (request: express.Request, response: express.Response) => { + const input: string = request.body?.input; + + if (!input || input.trim() === '') { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing input' + }); + } + + let pnid: HydratedPNIDDocument | null; + + if (validator.isEmail(input)) { + pnid = await getPNIDByEmailAddress(input); + } else { + pnid = await getPNIDByUsername(input); + } + + if (pnid) { + await sendForgotPasswordEmail(pnid); + } + + response.json({ + app: 'api', + status: 200 + }); +}); + +export default router; \ No newline at end of file diff --git a/src/services/api/routes/v1/login.js b/src/services/api/routes/v1/login.js deleted file mode 100644 index e342ea5..0000000 --- a/src/services/api/routes/v1/login.js +++ /dev/null @@ -1,129 +0,0 @@ -const router = require('express').Router(); -const bcrypt = require('bcrypt'); -const fs = require('fs-extra'); -const database = require('../../../../database'); -const cache = require('../../../../cache'); -const util = require('../../../../util'); - -/** - * [POST] - * Implementation of for: https://api.pretendo.cc/v1/login - * Description: Generates an access token for an API user - * TODO: Replace this with a more robust OAuth2 implementation - */ -router.post('/', async (request, response) => { - const { body } = request; - const { grant_type, username, password, refresh_token } = body; - - if (!['password', 'refresh_token'].includes(grant_type)) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid grant type' - }); - } - - if (grant_type === 'password' && (!username || username.trim() === '')) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing username' - }); - } - - if (grant_type === 'password' && (!password || password.trim() === '')) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing password' - }); - } - - if (grant_type === 'refresh_token' && (!refresh_token || refresh_token.trim() === '')) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing refresh token' - }); - } - - let pnid; - if (grant_type === 'password') { - pnid = await database.getUserByUsername(username); - if (!pnid) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'User not found' - }); - } - - const hashedPassword = util.nintendoPasswordHash(password, pnid.get('pid')); - - if (!pnid || !bcrypt.compareSync(hashedPassword, pnid.password)) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing password' - }); - } - } else { - pnid = await database.getUserBearer(refresh_token); - if (!pnid) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing refresh token' - }); - } - } - - const cryptoPath = `${__dirname}/../../../../../certs/service/account`; - - if (!await fs.pathExists(cryptoPath)) { - // Need to generate keys - return response.status(500).json({ - app: 'api', - status: 500, - error: 'Failed to locate crypto keys. Please contact an administrator' - }); - } - - const publicKey = await cache.getServicePublicKey('account'); - const secretKey = await cache.getServiceSecretKey('account'); - - const cryptoOptions = { - public_key: publicKey, - hmac_secret: secretKey - }; - - const accessTokenOptions = { - system_type: 0xF, // API - token_type: 0x1, // OAuth Access, - pid: pnid.get('pid'), - access_level: pnid.get('access_level'), - title_id: BigInt(0), - expire_time: BigInt(Date.now() + (3600 * 1000)) - }; - - const refreshTokenOptions = { - system_type: 0xF, // API - token_type: 0x2, // OAuth Refresh, - pid: pnid.get('pid'), - access_level: pnid.get('access_level'), - title_id: BigInt(0), - expire_time: BigInt(Date.now() + (3600 * 1000)) - }; - - const accessToken = await util.generateToken(cryptoOptions, accessTokenOptions); - const refreshToken = await util.generateToken(cryptoOptions, refreshTokenOptions); - - response.json({ - access_token: accessToken, - token_type: 'Bearer', - expires_in: 3600, - refresh_token: refreshToken - }); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/api/routes/v1/login.ts b/src/services/api/routes/v1/login.ts new file mode 100644 index 0000000..400b3fe --- /dev/null +++ b/src/services/api/routes/v1/login.ts @@ -0,0 +1,119 @@ +import express from 'express'; +import bcrypt from 'bcrypt'; +import { getPNIDByUsername, getPNIDByBearerAuth } from '@/database'; +import { nintendoPasswordHash, generateToken} from '@/util'; +import { config } from '@/config-manager'; +import { TokenOptions } from '@/types/common/token-options'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +const router: express.Router = express.Router(); + +/** + * [POST] + * Implementation of for: https://api.pretendo.cc/v1/login + * Description: Generates an access token for an API user + * TODO: Replace this with a more robust OAuth2 implementation + */ +router.post('/', async (request: express.Request, response: express.Response) => { + const grantType: string = request.body?.grantType; + const username: string = request.body?.username; + const password: string = request.body?.password; + const refreshToken: string = request.body?.refresh_token; + + if (!['password', 'refresh_token'].includes(grantType)) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid grant type' + }); + } + + if (grantType === 'password' && (!username || username.trim() === '')) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing username' + }); + } + + if (grantType === 'password' && (!password || password.trim() === '')) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing password' + }); + } + + if (grantType === 'refresh_token' && (!refreshToken || refreshToken.trim() === '')) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing refresh token' + }); + } + + let pnid: HydratedPNIDDocument | null; + + if (grantType === 'password') { + pnid = await getPNIDByUsername(username); + + if (!pnid) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'User not found' + }); + } + + const hashedPassword: string = nintendoPasswordHash(password, pnid.pid); + + if (!pnid || !bcrypt.compareSync(hashedPassword, pnid.password)) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing password' + }); + } + } else { + pnid = await getPNIDByBearerAuth(refreshToken); + + if (!pnid) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing refresh token' + }); + } + } + + const accessTokenOptions: TokenOptions = { + system_type: 0x1, // * WiiU + token_type: 0x1, // * OAuth Access + pid: pnid.pid, + expire_time: BigInt(Date.now() + (3600 * 1000)) + }; + + const refreshTokenOptions: TokenOptions = { + system_type: 0x1, // * WiiU + token_type: 0x2, // * OAuth Refresh + pid: pnid.pid, + expire_time: BigInt(Date.now() + (3600 * 1000)) + }; + + const accessTokenBuffer: Buffer | null = await generateToken(config.aes_key, accessTokenOptions); + const refreshTokenBuffer: Buffer | null = await generateToken(config.aes_key, refreshTokenOptions); + + const accessToken: string = accessTokenBuffer ? accessTokenBuffer.toString('hex') : ''; + const newRefreshToken: string = refreshTokenBuffer ? refreshTokenBuffer.toString('hex') : ''; + + // TODO - Handle null tokens + + response.json({ + access_token: accessToken, + token_type: 'Bearer', + expires_in: 3600, + refresh_token: newRefreshToken + }); +}); + +export default router; \ No newline at end of file diff --git a/src/services/api/routes/v1/register.js b/src/services/api/routes/v1/register.ts similarity index 64% rename from src/services/api/routes/v1/register.js rename to src/services/api/routes/v1/register.ts index 729bc41..25d30ca 100644 --- a/src/services/api/routes/v1/register.js +++ b/src/services/api/routes/v1/register.ts @@ -1,46 +1,49 @@ -const router = require('express').Router(); -const emailvalidator = require('email-validator'); -const fs = require('fs-extra'); -const moment = require('moment'); -const crypto = require('crypto'); -const hcaptcha = require('hcaptcha'); -const bcrypt = require('bcrypt'); -const Mii = require('mii-js'); -const { PNID } = require('../../../../models/pnid'); -const { NEXAccount } = require('../../../../models/nex-account'); -const database = require('../../../../database'); -const cache = require('../../../../cache'); -const util = require('../../../../util'); -const logger = require('../../../../../logger'); -const { config, disabledFeatures } = require('../../../../config-manager'); -const PNID_VALID_CHARACTERS_REGEX = /^[\w\-\.]*$/gm; -const PNID_PUNCTUATION_START_REGEX = /^[\_\-\.]/gm; -const PNID_PUNCTUATION_END_REGEX = /[\_\-\.]$/gm; -const PNID_PUNCTUATION_DUPLICATE_REGEX = /[\_\-\.]{2,}/gm; +import crypto from 'node:crypto'; +import express from 'express'; +import emailvalidator from 'email-validator'; +import bcrypt from 'bcrypt'; +import moment from 'moment'; +import hcaptcha from 'hcaptcha'; +import Mii from 'mii-js'; +import mongoose from 'mongoose'; +import { doesPNIDExist, connection as databaseConnection } from '@/database'; +import { nintendoPasswordHash, sendConfirmationEmail, generateToken } from '@/util'; +import { LOG_ERROR } from '@/logger'; +import { PNID } from '@/models/pnid'; +import { NEXAccount } from '@/models/nex-account'; +import { config, disabledFeatures } from '@/config-manager'; +import { TokenOptions } from '@/types/common/token-options'; +import { HydratedNEXAccountDocument } from '@/types/mongoose/nex-account'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +const router: express.Router = express.Router(); + +const PNID_VALID_CHARACTERS_REGEX: RegExp = /^[\w\-.]*$/gm; +const PNID_PUNCTUATION_START_REGEX: RegExp = /^[_\-.]/gm; +const PNID_PUNCTUATION_END_REGEX: RegExp = /[_\-.]$/gm; +const PNID_PUNCTUATION_DUPLICATE_REGEX: RegExp = /[_\-.]{2,}/gm; // This sucks -const PASSWORD_WORD_OR_NUMBER_REGEX = /(?=.*[a-zA-Z])(?=.*\d).*/; -const PASSWORD_WORD_OR_PUNCTUATION_REGEX = /(?=.*[a-zA-Z])(?=.*[\_\-\.]).*/; -const PASSWORD_NUMBER_OR_PUNCTUATION_REGEX = /(?=.*\d)(?=.*[\_\-\.]).*/; -const PASSWORD_REPEATED_CHARACTER_REGEX = /(.)\1\1/; +const PASSWORD_WORD_OR_NUMBER_REGEX: RegExp = /(?=.*[a-zA-Z])(?=.*\d).*/; +const PASSWORD_WORD_OR_PUNCTUATION_REGEX: RegExp = /(?=.*[a-zA-Z])(?=.*[_\-.]).*/; +const PASSWORD_NUMBER_OR_PUNCTUATION_REGEX: RegExp = /(?=.*\d)(?=.*[_\-.]).*/; +const PASSWORD_REPEATED_CHARACTER_REGEX: RegExp = /(.)\1\1/; -const DEFAULT_MII_DATA = Buffer.from('AwAAQOlVognnx0GC2/uogAOzuI0n2QAAAEBEAGUAZgBhAHUAbAB0AAAAAAAAAEBAAAAhAQJoRBgmNEYUgRIXaA0AACkAUkhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGm9', 'base64'); +const DEFAULT_MII_DATA: Buffer = Buffer.from('AwAAQOlVognnx0GC2/uogAOzuI0n2QAAAEBEAGUAZgBhAHUAbAB0AAAAAAAAAEBAAAAhAQJoRBgmNEYUgRIXaA0AACkAUkhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGm9', 'base64'); /** * [POST] * Implementation of: https://api.pretendo.cc/v1/register * Description: Creates a new user PNID */ -router.post('/', async (request, response) => { - const { body } = request; - - const email = body.email?.trim(); - const username = body.username?.trim(); - const miiName = body.mii_name?.trim(); - const password = body.password?.trim(); - const passwordConfirm = body.password_confirm?.trim(); - const hCaptchaResponse = body.hCaptchaResponse?.trim(); +router.post('/', async (request: express.Request, response: express.Response) => { + const email: string = request.body.email?.trim(); + const username: string = request.body.username?.trim(); + const miiName: string = request.body.mii_name?.trim(); + const password: string = request.body.password?.trim(); + const passwordConfirm: string = request.body.password_confirm?.trim(); + const hCaptchaResponse: string = request.body.hCaptchaResponse?.trim(); if (!disabledFeatures.captcha) { if (!hCaptchaResponse || hCaptchaResponse === '') { @@ -51,7 +54,7 @@ router.post('/', async (request, response) => { }); } - const captchaVerify = await hcaptcha.verify(config.hcaptcha.secret, hCaptchaResponse); + const captchaVerify: VerifyResponse = await hcaptcha.verify(config.hcaptcha.secret, hCaptchaResponse); if (!captchaVerify.success) { return response.status(400).json({ @@ -135,7 +138,7 @@ router.post('/', async (request, response) => { }); } - const userExists = await database.doesUserExist(username); + const userExists: boolean = await doesPNIDExist(username); if (userExists) { return response.status(400).json({ @@ -209,7 +212,7 @@ router.post('/', async (request, response) => { }); } - const miiNameBuffer = Buffer.from(miiName, 'utf16le'); // UTF8 to UTF16 + const miiNameBuffer: Buffer = Buffer.from(miiName, 'utf16le'); // UTF8 to UTF16 if (miiNameBuffer.length > 0x14) { return response.status(400).json({ @@ -219,14 +222,14 @@ router.post('/', async (request, response) => { }); } - const mii = new Mii(DEFAULT_MII_DATA); + const mii: Mii = new Mii(DEFAULT_MII_DATA); mii.miiName = miiName; - const creationDate = moment().format('YYYY-MM-DDTHH:MM:SS'); - let pnid; - let nexAccount; + const creationDate: string = moment().format('YYYY-MM-DDTHH:MM:SS'); + let pnid: HydratedPNIDDocument; + let nexAccount: HydratedNEXAccountDocument; - const session = await database.connection.startSession(); + const session: mongoose.ClientSession = await databaseConnection().startSession(); await session.startTransaction(); try { @@ -244,15 +247,15 @@ router.post('/', async (request, response) => { // NN with a NNID will always use the NNID PID // even if the provided NEX PID is different // To fix this we make them the same PID - nexAccount.owning_pid = nexAccount.get('pid'); + nexAccount.owning_pid = nexAccount.pid; await nexAccount.save({ session }); - const primaryPasswordHash = util.nintendoPasswordHash(password, nexAccount.get('pid')); - const passwordHash = await bcrypt.hash(primaryPasswordHash, 10); + const primaryPasswordHash: string = nintendoPasswordHash(password, nexAccount.pid); + const passwordHash: string = await bcrypt.hash(primaryPasswordHash, 10); pnid = new PNID({ - pid: nexAccount.get('pid'), + pid: nexAccount.pid, creation_date: creationDate, updated: creationDate, username: username, @@ -303,7 +306,7 @@ router.post('/', async (request, response) => { await session.commitTransaction(); } catch (error) { - logger.error('[POST] /v1/register: ' + error); + LOG_ERROR('[POST] /v1/register: ' + error); await session.abortTransaction(); @@ -318,47 +321,29 @@ router.post('/', async (request, response) => { await session.endSession(); } - await util.sendConfirmationEmail(pnid); + await sendConfirmationEmail(pnid); - const cryptoPath = `${__dirname}/../../../../../certs/service/account`; - - if (!await fs.pathExists(cryptoPath)) { - // Need to generate keys - return response.status(500).json({ - app: 'api', - status: 500, - error: 'Failed to locate crypto keys. Please contact an administrator' - }); - } - - const publicKey = await cache.getServicePublicKey('account'); - const secretKey = await cache.getServiceSecretKey('account'); - - const cryptoOptions = { - public_key: publicKey, - hmac_secret: secretKey - }; - - const accessTokenOptions = { - system_type: 0xF, // API - token_type: 0x1, // OAuth Access, - pid: pnid.get('pid'), - access_level: 0, - title_id: BigInt(0), + const accessTokenOptions: TokenOptions = { + system_type: 0x1, // * WiiU + token_type: 0x1, // * OAuth Access + pid: pnid.pid, expire_time: BigInt(Date.now() + (3600 * 1000)) }; - const refreshTokenOptions = { - system_type: 0xF, // API - token_type: 0x2, // OAuth Refresh, - pid: pnid.get('pid'), - access_level: 0, - title_id: BigInt(0), + const refreshTokenOptions: TokenOptions = { + system_type: 0x1, // * WiiU + token_type: 0x2, // * OAuth Refresh + pid: pnid.pid, expire_time: BigInt(Date.now() + (3600 * 1000)) }; - const accessToken = await util.generateToken(cryptoOptions, accessTokenOptions); - const refreshToken = await util.generateToken(cryptoOptions, refreshTokenOptions); + const accessTokenBuffer: Buffer | null = await generateToken(config.aes_key, accessTokenOptions); + const refreshTokenBuffer: Buffer | null = await generateToken(config.aes_key, refreshTokenOptions); + + const accessToken: string = accessTokenBuffer ? accessTokenBuffer.toString('hex') : ''; + const refreshToken: string = refreshTokenBuffer ? refreshTokenBuffer.toString('hex') : ''; + + // TODO - Handle null tokens response.json({ access_token: accessToken, @@ -368,4 +353,4 @@ router.post('/', async (request, response) => { }); }); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/src/services/api/routes/v1/resetPassword.js b/src/services/api/routes/v1/resetPassword.ts similarity index 57% rename from src/services/api/routes/v1/resetPassword.js rename to src/services/api/routes/v1/resetPassword.ts index 8afb3b1..266e887 100644 --- a/src/services/api/routes/v1/resetPassword.js +++ b/src/services/api/routes/v1/resetPassword.ts @@ -1,19 +1,23 @@ -const router = require('express').Router(); -const bcrypt = require('bcrypt'); -const { PNID } = require('../../../../models/pnid'); -const util = require('../../../../util'); +import express from 'express'; +import bcrypt from 'bcrypt'; +import { PNID } from '@/models/pnid'; +import { decryptToken, unpackToken, nintendoPasswordHash } from '@/util'; +import { config } from '@/config-manager'; +import { Token } from '@/types/common/token'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +const router: express.Router = express.Router(); // This sucks -const PASSWORD_WORD_OR_NUMBER_REGEX = /(?=.*[a-zA-Z])(?=.*\d).*/; -const PASSWORD_WORD_OR_PUNCTUATION_REGEX = /(?=.*[a-zA-Z])(?=.*[\_\-\.]).*/; -const PASSWORD_NUMBER_OR_PUNCTUATION_REGEX = /(?=.*\d)(?=.*[\_\-\.]).*/; -const PASSWORD_REPEATED_CHARACTER_REGEX = /(.)\1\1/; +const PASSWORD_WORD_OR_NUMBER_REGEX: RegExp = /(?=.*[a-zA-Z])(?=.*\d).*/; +const PASSWORD_WORD_OR_PUNCTUATION_REGEX: RegExp = /(?=.*[a-zA-Z])(?=.*[_\-.]).*/; +const PASSWORD_NUMBER_OR_PUNCTUATION_REGEX: RegExp = /(?=.*\d)(?=.*[_\-.]).*/; +const PASSWORD_REPEATED_CHARACTER_REGEX: RegExp = /(.)\1\1/; -router.post('/', async (request, response) => { - const { body } = request; - const password = body.password?.trim(); - const passwordConfirm = body.password_confirm?.trim(); - const token = body.token?.trim(); +router.post('/', async (request: express.Request, response: express.Response) => { + const password: string = request.body.password?.trim(); + const passwordConfirm: string = request.body.password_confirm?.trim(); + const token: string = request.body.token?.trim(); if (!token || token === '') { return response.status(400).json({ @@ -23,10 +27,10 @@ router.post('/', async (request, response) => { }); } - let unpackedToken; + let unpackedToken: Token; try { - const decryptedToken = await util.decryptToken(Buffer.from(token, 'base64')); - unpackedToken = util.unpackToken(decryptedToken); + const decryptedToken: Buffer = await decryptToken(config.aes_key, Buffer.from(token, 'base64')); + unpackedToken = unpackToken(decryptedToken); } catch (error) { console.log(error); return response.status(400).json({ @@ -44,7 +48,7 @@ router.post('/', async (request, response) => { }); } - const pnid = await PNID.findOne({ pid: unpackedToken.pid }); + const pnid: HydratedPNIDDocument | null = await PNID.findOne({ pid: unpackedToken.pid }); if (!pnid) { return response.status(400).json({ @@ -79,7 +83,7 @@ router.post('/', async (request, response) => { }); } - if (password.toLowerCase() === pnid.get('usernameLower')) { + if (password.toLowerCase() === pnid.usernameLower) { return response.status(400).json({ app: 'api', status: 400, @@ -111,8 +115,8 @@ router.post('/', async (request, response) => { }); } - const primaryPasswordHash = util.nintendoPasswordHash(password, pnid.get('pid')); - const passwordHash = await bcrypt.hash(primaryPasswordHash, 10); + const primaryPasswordHash: string = nintendoPasswordHash(password, pnid.pid); + const passwordHash: string = await bcrypt.hash(primaryPasswordHash, 10); pnid.password = passwordHash; @@ -124,4 +128,4 @@ router.post('/', async (request, response) => { }); }); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/src/services/api/routes/v1/user.js b/src/services/api/routes/v1/user.js deleted file mode 100644 index 7008548..0000000 --- a/src/services/api/routes/v1/user.js +++ /dev/null @@ -1,127 +0,0 @@ -const router = require('express').Router(); -const joi = require('joi'); -const { PNID } = require('../../../../models/pnid'); -const { config } = require('../../../../config-manager'); - -// TODO: Extend this later with more settings -const userSchema = joi.object({ - mii: joi.object({ - name: joi.string(), - primary: joi.string(), - data: joi.string(), - }) -}); - -/** - * [GET] - * Implementation of for: https://api.pretendo.cc/v1/user - * Description: Gets PNID details about the current user - */ -router.get('/', async (request, response) => { - const { pnid } = request; - - if (!pnid) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing access token' - }); - } - - return response.json({ - access_level: pnid.get('access_level'), - server_access_level: pnid.get('server_access_level'), - pid: pnid.get('pid'), - creation_date: pnid.get('creation_date'), - updated: pnid.get('updated'), - username: pnid.get('username'), - birthdate: pnid.get('birthdate'), - gender: pnid.get('gender'), - country: pnid.get('country'), - email: { - address: pnid.get('email.address'), - }, - timezone: { - name: pnid.get('timezone.name') - }, - mii: { - data: pnid.get('mii.data'), - name: pnid.get('mii.name'), - image_url: `${config.cdn.base_url}/mii/${pnid.get('pid')}/normal_face.png` - }, - flags: { - marketing: pnid.get('flags.marketing') - }, - connections: { - discord: { - id: pnid.get('connections.discord.id') - } - } - }); -}); - -/** - * [POST] - * Implementation of for: https://api.pretendo.cc/v1/user - * Description: Updates PNID certain details about the current user - */ -router.post('/', async (request, response) => { - const { body, pnid } = request; - - if (!pnid) { - return response.status(400).json({ - app: 'api', - status: 400, - error: 'Invalid or missing access token' - }); - } - - const valid = userSchema.validate(body); - - if (valid.error) { - return response.status(400).json({ - app: 'api', - status: 400, - error: valid.error - }); - } - - const { pid } = pnid; - - const updateData = {}; - - await PNID.updateOne({ pid }, { $set: updateData }).exec(); - - return response.json({ - access_level: pnid.get('access_level'), - server_access_level: pnid.get('server_access_level'), - pid: pnid.get('pid'), - creation_date: pnid.get('creation_date'), - updated: pnid.get('updated'), - username: pnid.get('username'), - birthdate: pnid.get('birthdate'), - gender: pnid.get('gender'), - country: pnid.get('country'), - email: { - address: pnid.get('email.address'), - }, - timezone: { - name: pnid.get('timezone.name') - }, - mii: { - data: pnid.get('mii.data'), - name: pnid.get('mii.name'), - image_url: `${config.cdn.base_url}/mii/${pnid.get('pid')}/normal_face.png` - }, - flags: { - marketing: pnid.get('flags.marketing') - }, - connections: { - discord: { - id: pnid.get('connections.discord.id') - } - } - }); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/api/routes/v1/user.ts b/src/services/api/routes/v1/user.ts new file mode 100644 index 0000000..28eac4e --- /dev/null +++ b/src/services/api/routes/v1/user.ts @@ -0,0 +1,134 @@ +import express from 'express'; +import joi from 'joi'; +//import { PNID } from '@/models/pnid'; +import { config } from '@/config-manager'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; +import { UpdateUserRequest } from '@/types/services/api/update-user-request'; + +const router: express.Router = express.Router(); + +// TODO: Extend this later with more settings +const userSchema: joi.ObjectSchema = joi.object({ + mii: joi.object({ + name: joi.string(), + primary: joi.string(), + data: joi.string(), + }) +}); + +/** + * [GET] + * Implementation of for: https://api.pretendo.cc/v1/user + * Description: Gets PNID details about the current user + */ +router.get('/', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing access token' + }); + } + + return response.json({ + access_level: pnid.access_level, + server_access_level: pnid.server_access_level, + pid: pnid.pid, + creation_date: pnid.creation_date, + updated: pnid.updated, + username: pnid.username, + birthdate: pnid.birthdate, + gender: pnid.gender, + country: pnid.country, + email: { + address: pnid.email.address, + }, + timezone: { + name: pnid.timezone.name + }, + mii: { + data: pnid.mii.data, + name: pnid.mii.name, + image_url: `${config.cdn.base_url}/mii/${pnid.pid}/normal_face.png` + }, + flags: { + marketing: pnid.flags.marketing + }, + connections: { + discord: { + id: pnid.connections.discord.id + } + } + }); +}); + +/** + * [POST] + * Implementation of for: https://api.pretendo.cc/v1/user + * Description: Updates PNID certain details about the current user + */ +router.post('/', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + const updateUserRequest: UpdateUserRequest = request.body; + + if (!pnid) { + return response.status(400).json({ + app: 'api', + status: 400, + error: 'Invalid or missing access token' + }); + } + + const valid: joi.ValidationResult = userSchema.validate(updateUserRequest); + + if (valid.error) { + return response.status(400).json({ + app: 'api', + status: 400, + error: valid.error + }); + } + + // TODO - Make this do something + + //const pid: number = pnid.pid; + + //const updateData = {}; + + //await PNID.updateOne({ pid }, { $set: updateData }).exec(); + + return response.json({ + access_level: pnid.access_level, + server_access_level: pnid.server_access_level, + pid: pnid.pid, + creation_date: pnid.creation_date, + updated: pnid.updated, + username: pnid.username, + birthdate: pnid.birthdate, + gender: pnid.gender, + country: pnid.country, + email: { + address: pnid.email.address, + }, + timezone: { + name: pnid.timezone.name + }, + mii: { + data: pnid.mii.data, + name: pnid.mii.name, + image_url: `${config.cdn.base_url}/mii/${pnid.pid}/normal_face.png` + }, + flags: { + marketing: pnid.flags.marketing + }, + connections: { + discord: { + id: pnid.connections.discord.id + } + } + }); +}); + +export default router; \ No newline at end of file diff --git a/src/services/assets/index.js b/src/services/assets/index.js deleted file mode 100644 index 2059739..0000000 --- a/src/services/assets/index.js +++ /dev/null @@ -1,22 +0,0 @@ -// handles serving assets - -const express = require('express'); -const subdomain = require('express-subdomain'); -const logger = require('../../../logger'); -const path = require('path'); - -// Router to handle the subdomain restriction -const assets = express.Router(); - -// Setup public folder -logger.info('[assets] Setting up public folder'); -assets.use(express.static(path.join(__dirname, '../../assets'))); - -// Main router for endpoints -const router = express.Router(); - -// Create subdomains -logger.info('[conntest] Creating \'assets\' subdomain'); -router.use(subdomain('assets', assets)); - -module.exports = router; \ No newline at end of file diff --git a/src/services/assets/index.ts b/src/services/assets/index.ts new file mode 100644 index 0000000..03faa93 --- /dev/null +++ b/src/services/assets/index.ts @@ -0,0 +1,22 @@ +// handles serving assets + +import path from 'node:path'; +import express from 'express'; +import subdomain from 'express-subdomain'; +import { LOG_INFO } from '@/logger'; + +// Router to handle the subdomain restriction +const assets: express.Router = express.Router(); + +// Setup public folder +LOG_INFO('[assets] Setting up public folder'); +assets.use(express.static(path.join(__dirname, '../../assets'))); + +// Main router for endpoints +const router: express.Router = express.Router(); + +// Create subdomains +LOG_INFO('[conntest] Creating \'assets\' subdomain'); +router.use(subdomain('assets', assets)); + +export default router; \ No newline at end of file diff --git a/src/services/conntest/index.js b/src/services/conntest/index.ts similarity index 53% rename from src/services/conntest/index.js rename to src/services/conntest/index.ts index ebe2515..c016eca 100644 --- a/src/services/conntest/index.js +++ b/src/services/conntest/index.ts @@ -1,15 +1,15 @@ // handles conntest endpoints -const express = require('express'); -const subdomain = require('express-subdomain'); -const logger = require('../../../logger'); +import express from 'express'; +import subdomain from 'express-subdomain'; +import { LOG_INFO } from '@/logger'; // Router to handle the subdomain restriction -const conntest = express.Router(); +const conntest: express.Router = express.Router(); // Setup route -logger.info('[conntest] Applying imported routes'); -conntest.get('/', async (request, response) => { +LOG_INFO('[conntest] Applying imported routes'); +conntest.get('/', async (request: express.Request, response: express.Response) => { response.set('Content-Type', 'text/html'); response.set('X-Organization', 'Nintendo'); @@ -23,14 +23,14 @@ conntest.get('/', async (request, response) => { This is test.html page -`) +`); }); // Main router for endpoints -const router = express.Router(); +const router: express.Router = express.Router(); // Create subdomains -logger.info('[conntest] Creating \'conntest\' subdomain'); +LOG_INFO('[conntest] Creating \'conntest\' subdomain'); router.use(subdomain('conntest', conntest)); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/src/services/datastore/index.js b/src/services/datastore/index.js deleted file mode 100644 index ab263d2..0000000 --- a/src/services/datastore/index.js +++ /dev/null @@ -1,20 +0,0 @@ -const express = require('express'); -const subdomain = require('express-subdomain'); -const logger = require('../../../logger'); -const routes = require('./routes'); - -// Router to handle the subdomain -const datastore = express.Router(); - -// Setup routes -logger.info('[DATASTORE] Applying imported routes'); -datastore.use(routes.UPLOAD); - -// Main router for endpoints -const router = express.Router(); - -// Create subdomains -logger.info('[DATASTORE] Creating \'datastore\' subdomain'); -router.use(subdomain('datastore', datastore)); - -module.exports = router; \ No newline at end of file diff --git a/src/services/datastore/index.ts b/src/services/datastore/index.ts new file mode 100644 index 0000000..3d2e4f7 --- /dev/null +++ b/src/services/datastore/index.ts @@ -0,0 +1,21 @@ +import express from 'express'; +import subdomain from 'express-subdomain'; +import { LOG_INFO } from '@/logger'; + +import upload from '@/services/datastore/routes/upload'; + +// Router to handle the subdomain +const datastore: express.Router = express.Router(); + +// Setup routes +LOG_INFO('[DATASTORE] Applying imported routes'); +datastore.use(upload); + +// Main router for endpoints +const router: express.Router = express.Router(); + +// Create subdomains +LOG_INFO('[DATASTORE] Creating \'datastore\' subdomain'); +router.use(subdomain('datastore', datastore)); + +export default router; \ No newline at end of file diff --git a/src/services/datastore/routes/index.js b/src/services/datastore/routes/index.js deleted file mode 100644 index 34215ff..0000000 --- a/src/services/datastore/routes/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - UPLOAD: require('./upload.js'), -}; \ No newline at end of file diff --git a/src/services/datastore/routes/upload.js b/src/services/datastore/routes/upload.js deleted file mode 100644 index 18f13b6..0000000 --- a/src/services/datastore/routes/upload.js +++ /dev/null @@ -1,74 +0,0 @@ -const crypto = require('crypto'); -const router = require('express').Router(); -const Dicer = require('dicer'); -const fs = require('fs'); -const util = require('../../../util'); - -const signatureSecret = fs.readFileSync(`${__dirname}/../../../../certs/nex/datastore/secret.key`); - -function multipartParser(request, response, next) { - const RE_BOUNDARY = /^multipart\/.+?(?:; boundary=(?:(?:"(.+)")|(?:([^\s]+))))$/i; - const RE_FILE_NAME = /name="(.*)"/; - const boundary = RE_BOUNDARY.exec(request.header('content-type')); - const dicer = new Dicer({ boundary: boundary[1] || boundary[2] }); - const files = {}; - - dicer.on('part', part => { - let fileBuffer = Buffer.alloc(0); - let fileName = ''; - - part.on('header', header => { - fileName = RE_FILE_NAME.exec(header['content-disposition'][0])[1]; - }); - - part.on('data', data => { - fileBuffer = Buffer.concat([fileBuffer, data]); - }); - - part.on('end', () => { - files[fileName] = fileBuffer; - }); - }); - - dicer.on('finish', function () { - request.files = files; - return next(); - }); - - request.pipe(dicer); -} - -router.post('/upload', multipartParser, async (request, response) => { - const { - bucket, // Space name - key, // path - file, // the file content - acl, // S3 ACL - pid, // uploading user PID - date, // upload time - signature, // data signature - } = request.files; - - // Signatures only good for 1 minute - const minute = 1000 * 60; - const minuteAgo = Date.now() - minute; - - if (Number(date) < Math.floor(minuteAgo / 1000)) { - return response.sendStatus(400); - } - - const data = pid.toString() + bucket.toString() + key.toString() + date.toString(); - - const hmac = crypto.createHmac('sha256', signatureSecret).update(data).digest('hex'); - - console.log(hmac, signature.toString()); - - if (hmac !== signature.toString()) { - return response.sendStatus(400); - } - - await util.uploadCDNAsset(bucket.toString(), key.toString(), file, acl.toString()); - response.sendStatus(200); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/datastore/routes/upload.ts b/src/services/datastore/routes/upload.ts new file mode 100644 index 0000000..6bd3a5b --- /dev/null +++ b/src/services/datastore/routes/upload.ts @@ -0,0 +1,99 @@ +import fs from 'node:fs'; +import crypto from 'node:crypto'; +import express from 'express'; +import Dicer from 'dicer'; +import { uploadCDNAsset } from '@/util'; + +const router: express.Router = express.Router(); + +const signatureSecret: Buffer = fs.readFileSync(`${__dirname}/../../../../certs/nex/datastore/secret.key`); + +function multipartParser(request: express.Request, response: express.Response, next: express.NextFunction): void { + const RE_BOUNDARY: RegExp = /^multipart\/.+?(?:; boundary=(?:(?:"(.+)")|(?:([^\s]+))))$/i; + const RE_FILE_NAME: RegExp = /name="(.*)"/; + + const contentType: string | undefined = request.header('content-type'); + + if (!contentType) { + return next(); + } + + const boundary: RegExpExecArray | null = RE_BOUNDARY.exec(contentType); + + if (!boundary) { + return next(); + } + + const dicer: Dicer = new Dicer({ boundary: boundary[1] || boundary[2] }); + const files: { [key: string]: Buffer } = {}; + + dicer.on('part', (part: Dicer.PartStream) => { + let fileBuffer: Buffer = Buffer.alloc(0); + let fileName: string = ''; + + part.on('header', header => { + const contentDisposition = header['content-disposition' as keyof object]; + const regexResult = RE_FILE_NAME.exec(contentDisposition); + + if (regexResult) { + fileName = regexResult[0]; + } + }); + + part.on('data', (data: Buffer | string) => { + if (data instanceof String) { + data = Buffer.from(data); + } + + fileBuffer = Buffer.concat([fileBuffer, data as Buffer]); + }); + + part.on('end', () => { + files[fileName] = fileBuffer; + }); + }); + + dicer.on('finish', function () { + request.files = files; + return next(); + }); + + request.pipe(dicer); +} + +router.post('/upload', multipartParser, async (request: express.Request, response: express.Response) => { + if (!request.files) { + return response.sendStatus(500); + } + + const bucket: string = request.files.bucket.toString(); + const key: string = request.files.key.toString(); + const file: Buffer = request.files.file; + const acl: string = request.files.acl.toString(); + const pid: string = request.files.pid.toString(); + const date: string = request.files.date.toString(); + const signature: string = request.files.signature.toString(); + + // Signatures only good for 1 minute + const minute: number = 1000 * 60; + const minuteAgo: number = Date.now() - minute; + + if (Number(date) < Math.floor(minuteAgo / 1000)) { + return response.sendStatus(400); + } + + const data: string = `${pid}${bucket}${key}${date}`; + + const hmac: string = crypto.createHmac('sha256', signatureSecret).update(data).digest('hex'); + + console.log(hmac, signature); + + if (hmac !== signature) { + return response.sendStatus(400); + } + + await uploadCDNAsset(bucket, key, file, acl); + response.sendStatus(200); +}); + +export default router; \ No newline at end of file diff --git a/src/services/local-cdn/index.js b/src/services/local-cdn/index.js deleted file mode 100644 index b0e3985..0000000 --- a/src/services/local-cdn/index.js +++ /dev/null @@ -1,30 +0,0 @@ -const express = require('express'); -const subdomain = require('express-subdomain'); -const logger = require('../../../logger'); -const { config, disabledFeatures } = require('../../config-manager'); - -if (!disabledFeatures.s3) { - // * s3 enabled, no need for this - - module.exports = express.Router(); - - return; -} - -const routes = require('./routes'); - -// Router to handle the subdomain -const localcdn = express.Router(); - -// Setup routes -logger.info('[LOCAL-CDN] Applying imported routes'); -localcdn.use(routes.GET); - -// Main router for endpoints -const router = express.Router(); - -// Create subdomains -logger.info(`[LOCAL-CDN] Creating '${config.cdn.subdomain}' subdomain`); -router.use(subdomain(config.cdn.subdomain, localcdn)); - -module.exports = router; \ No newline at end of file diff --git a/src/services/local-cdn/index.ts b/src/services/local-cdn/index.ts new file mode 100644 index 0000000..c1b2963 --- /dev/null +++ b/src/services/local-cdn/index.ts @@ -0,0 +1,27 @@ +import express from 'express'; +import subdomain from 'express-subdomain'; +import { config, disabledFeatures } from '@/config-manager'; +import { LOG_INFO } from '@/logger'; + +import get from '@/services/local-cdn/routes/get'; + +const router: express.Router = express.Router(); + +if (disabledFeatures.s3) { + // * s3 disabled, setup local CDN + + // * Router to handle the subdomain + const localcdn: express.Router = express.Router(); + + // * Setup routes + LOG_INFO('[LOCAL-CDN] Applying imported routes'); + localcdn.use(get); + + // * Create subdomains + LOG_INFO(`[LOCAL-CDN] Creating '${config.cdn.subdomain}' subdomain`); + router.use(subdomain(config.cdn.subdomain, localcdn)); +} else { + LOG_INFO('[LOCAL-CDN] s3 enabled, skipping local CDN'); +} + +export default router; \ No newline at end of file diff --git a/src/services/local-cdn/routes/get.js b/src/services/local-cdn/routes/get.js deleted file mode 100644 index 7ae69b9..0000000 --- a/src/services/local-cdn/routes/get.js +++ /dev/null @@ -1,16 +0,0 @@ -const router = require('express').Router(); -const cache = require('../../../cache'); - -router.get('/*', async (request, response) => { - const filePath = request.params[0]; - - const file = await cache.getLocalCDNFile(filePath); - - if (file) { - response.send(file); - } else { - response.sendStatus(404); - } -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/local-cdn/routes/get.ts b/src/services/local-cdn/routes/get.ts new file mode 100644 index 0000000..6c98b71 --- /dev/null +++ b/src/services/local-cdn/routes/get.ts @@ -0,0 +1,18 @@ +import express from 'express'; +import { getLocalCDNFile } from '@/cache'; + +const router: express.Router = express.Router(); + +router.get('/*', async (request: express.Request, response: express.Response) => { + const filePath: string = request.params[0]; + + const file: Buffer = await getLocalCDNFile(filePath); + + if (file) { + response.send(file); + } else { + response.sendStatus(404); + } +}); + +export default router; \ No newline at end of file diff --git a/src/services/local-cdn/routes/index.js b/src/services/local-cdn/routes/index.js deleted file mode 100644 index 033fca1..0000000 --- a/src/services/local-cdn/routes/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - GET: require('./get.js'), -}; \ No newline at end of file diff --git a/src/services/nasc/index.js b/src/services/nasc/index.js deleted file mode 100644 index 9f4f9f3..0000000 --- a/src/services/nasc/index.js +++ /dev/null @@ -1,26 +0,0 @@ -// handles NASC endpoints - -const express = require('express'); -const subdomain = require('express-subdomain'); -const NASCMiddleware = require('../../middleware/nasc'); -const logger = require('../../../logger'); -const routes = require('./routes'); - -// Router to handle the subdomain restriction -const nasc = express.Router(); - -logger.info('[NASC] Importing middleware'); -nasc.use(NASCMiddleware); - -// Setup routes -logger.info('[NASC] Applying imported routes'); -nasc.use('/ac', routes.AC); - -// Main router for endpoints -const router = express.Router(); - -// Create subdomains -logger.info('[NASC] Creating \'nasc\' subdomain'); -router.use(subdomain('nasc', nasc)); - -module.exports = router; \ No newline at end of file diff --git a/src/services/nasc/index.ts b/src/services/nasc/index.ts new file mode 100644 index 0000000..29d1148 --- /dev/null +++ b/src/services/nasc/index.ts @@ -0,0 +1,27 @@ +// handles NASC endpoints + +import express from 'express'; +import subdomain from 'express-subdomain'; +import NASCMiddleware from '@/middleware/nasc'; +import { LOG_INFO } from '@/logger'; + +import ac from '@/services/nasc/routes/ac'; + +// Router to handle the subdomain restriction +const nasc: express.Router = express.Router(); + +LOG_INFO('[NASC] Importing middleware'); +nasc.use(NASCMiddleware); + +// Setup routes +LOG_INFO('[NASC] Applying imported routes'); +nasc.use('/ac', ac); + +// Main router for endpoints +const router: express.Router = express.Router(); + +// Create subdomains +LOG_INFO('[NASC] Creating \'nasc\' subdomain'); +router.use(subdomain('nasc', nasc)); + +export default router; \ No newline at end of file diff --git a/src/services/nasc/routes/ac.js b/src/services/nasc/routes/ac.js deleted file mode 100644 index 66d08d4..0000000 --- a/src/services/nasc/routes/ac.js +++ /dev/null @@ -1,106 +0,0 @@ -const fs = require('fs-extra'); -const express = require('express'); -const util = require('../../../util'); -const database = require('../../../database'); -const cache = require('../../../cache'); - -const router = express.Router(); - -/** - * [POST] - * Replacement for: https://nasc.nintendowifi.net/ac - * Description: Gets a NEX server address and token - */ -router.post('/', async (request, response) => { - const requestParams = request.body; - const action = util.nintendoBase64Decode(requestParams.action).toString(); - let responseData; - - switch (action) { - case 'LOGIN': - responseData = await processLoginRequest(request); - break; - case 'SVCLOC': - responseData = await processServiceTokenRequest(request); - break; - } - - response.status(200).send(responseData.toString()); -}); - -/** - * - * @param {express.Request} request - */ -async function processLoginRequest(request) { - const requestParams = request.body; - const titleID = util.nintendoBase64Decode(requestParams.titleid).toString(); - const { nexUser } = request; - - // TODO: REMOVE AFTER PUBLIC LAUNCH - // LET EVERYONE IN THE `test` FRIENDS SERVER - // THAT WAY EVERYONE CAN GET AN ASSIGNED PID - let serverAccessLevel = 'test'; - if (titleID !== '0004013000003202') { - serverAccessLevel = nexUser.get('server_access_level'); - } - - const server = await database.getServerByTitleId(titleID, serverAccessLevel); - - if (!server || !server.service_name || !server.ip || !server.port) { - return util.nascError('110'); - } - - const { service_name, ip, port } = server; - - const cryptoPath = `${__dirname}/../../../../certs/nex/${service_name}`; - - const publicKey = await cache.getNEXPublicKey(service_name); - const secretKey = await cache.getNEXSecretKey(service_name); - - const cryptoOptions = { - public_key: publicKey, - hmac_secret: secretKey - }; - - const tokenOptions = { - system_type: 0x2, // 3DS - token_type: 0x3, // nex token, - pid: nexUser.get('pid'), - access_level: 0, - title_id: BigInt(parseInt(titleID, 16)), - expire_time: BigInt(Date.now() + (3600 * 1000)) - }; - - let nexToken = await util.generateToken(cryptoOptions, tokenOptions); - nexToken = util.nintendoBase64Encode(Buffer.from(nexToken, 'base64')); - - const params = new URLSearchParams({ - locator: util.nintendoBase64Encode(`${ip}:${port}`), - retry: util.nintendoBase64Encode('0'), - returncd: util.nintendoBase64Encode('001'), - token: nexToken, - datetime: util.nintendoBase64Encode(Date.now().toString()), - }); - - return params; -} - -/** - * - * @param {express.Request} request - */ -async function processServiceTokenRequest(request) { - const params = new URLSearchParams({ - retry: util.nintendoBase64Encode('0'), - returncd: util.nintendoBase64Encode('007'), - servicetoken: util.nintendoBase64Encode(Buffer.alloc(64).toString()), // hard coded for now - statusdata: util.nintendoBase64Encode('Y'), - svchost: util.nintendoBase64Encode('n/a'), - datetime: util.nintendoBase64Encode(Date.now().toString()), - }); - - return params; -} - -module.exports = router; \ No newline at end of file diff --git a/src/services/nasc/routes/ac.ts b/src/services/nasc/routes/ac.ts new file mode 100644 index 0000000..cd688d3 --- /dev/null +++ b/src/services/nasc/routes/ac.ts @@ -0,0 +1,108 @@ +import express from 'express'; +import { nintendoBase64Encode, nintendoBase64Decode, nascError, generateToken } from '@/util'; +import { getServerByTitleId } from '@/database'; +import { TokenOptions } from '@/types/common/token-options'; +import { NASCRequestParams } from '@/types/services/nasc/request-params'; +import { HydratedNEXAccountDocument } from '@/types/mongoose/nex-account'; +import { HydratedServerDocument } from '@/types/mongoose/server'; + +const router: express.Router = express.Router(); + +/** + * [POST] + * Replacement for: https://nasc.nintendowifi.net/ac + * Description: Gets a NEX server address and token + */ +router.post('/', async (request: express.Request, response: express.Response) => { + const requestParams: NASCRequestParams = request.body; + const action: string = nintendoBase64Decode(requestParams.action).toString(); + const titleID: string = nintendoBase64Decode(requestParams.titleid).toString(); + const nexAccount: HydratedNEXAccountDocument | null = request.nexAccount; + let responseData: URLSearchParams = nascError('null'); + + if (!nexAccount) { + return response.status(200).send(responseData.toString()); + } + + // TODO: REMOVE AFTER PUBLIC LAUNCH + // * LET EVERYONE IN THE `test` FRIENDS SERVER + // * THAT WAY EVERYONE CAN GET AN ASSIGNED PID + let serverAccessLevel: string = 'test'; + if (titleID !== '0004013000003202') { + serverAccessLevel = nexAccount.server_access_level; + } + + const server: HydratedServerDocument | null = await getServerByTitleId(titleID, serverAccessLevel); + + if (!server || !server.aes_key) { + return response.status(200).send( nascError('110').toString()); + } + + if (action === 'LOGIN' && server.port <= 0 && server.ip !== '0.0.0.0') { + // * Addresses of 0.0.0.0:0 are allowed + // * They are expected for titles with no NEX server + return response.status(200).send( nascError('110').toString()); + } + + switch (action) { + case 'LOGIN': + responseData = await processLoginRequest(server, nexAccount.pid, titleID); + break; + case 'SVCLOC': + responseData = await processServiceTokenRequest(server, nexAccount.pid, titleID); + break; + } + + response.status(200).send(responseData.toString()); +}); + +async function processLoginRequest(server: HydratedServerDocument, pid: number, titleID: string): Promise { + const tokenOptions: TokenOptions = { + system_type: 0x2, // * 3DS + token_type: 0x3, // * NEX token + pid: pid, + access_level: 0, + title_id: BigInt(parseInt(titleID, 16)), + expire_time: BigInt(Date.now() + (3600 * 1000)) + }; + + // TODO - Handle null tokens + + const nexTokenBuffer: Buffer | null = await generateToken(server.aes_key, tokenOptions); + const nexToken: string = nintendoBase64Encode(nexTokenBuffer || ''); + + return new URLSearchParams({ + locator: nintendoBase64Encode(`${server.ip}:${server.port}`), + retry: nintendoBase64Encode('0'), + returncd: nintendoBase64Encode('001'), + token: nexToken, + datetime: nintendoBase64Encode(Date.now().toString()), + }); +} + +async function processServiceTokenRequest(server: HydratedServerDocument, pid: number, titleID: string): Promise { + const tokenOptions: TokenOptions = { + system_type: 0x2, // * 3DS + token_type: 0x4, // * Service token + pid: pid, + access_level: 0, + title_id: BigInt(parseInt(titleID, 16)), + expire_time: BigInt(Date.now() + (3600 * 1000)) + }; + + // TODO - Handle null tokens + + const serviceTokenBuffer: Buffer | null = await generateToken(server.aes_key, tokenOptions); + const serviceToken: string = nintendoBase64Encode(serviceTokenBuffer || ''); + + return new URLSearchParams({ + retry: nintendoBase64Encode('0'), + returncd: nintendoBase64Encode('007'), + servicetoken: serviceToken, + statusdata: nintendoBase64Encode('Y'), + svchost: nintendoBase64Encode('n/a'), + datetime: nintendoBase64Encode(Date.now().toString()), + }); +} + +export default router; \ No newline at end of file diff --git a/src/services/nasc/routes/index.js b/src/services/nasc/routes/index.js deleted file mode 100644 index 971f25d..0000000 --- a/src/services/nasc/routes/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - AC: require('./ac') -}; \ No newline at end of file diff --git a/src/services/nnid/index.js b/src/services/nnid/index.js deleted file mode 100644 index 84f1d71..0000000 --- a/src/services/nnid/index.js +++ /dev/null @@ -1,40 +0,0 @@ -// handles "account.nintendo.net" endpoints - -const express = require('express'); -const subdomain = require('express-subdomain'); -const clientHeaderCheck = require('../../middleware/client-header'); -const cemuMiddleware = require('../../middleware/cemu'); -const pnidMiddleware = require('../../middleware/pnid'); -const logger = require('../../../logger'); -const routes = require('./routes'); - -// Router to handle the subdomain restriction -const nnid = express.Router(); - -logger.info('[NNID] Importing middleware'); -nnid.use(clientHeaderCheck); -nnid.use(cemuMiddleware); -nnid.use(pnidMiddleware); - -// Setup routes -logger.info('[NNID] Applying imported routes'); -nnid.use('/v1/api/admin', routes.ADMIN); -nnid.use('/v1/api/content', routes.CONTENT); -nnid.use('/v1/api/devices', routes.DEVICES); -nnid.use('/v1/api/miis', routes.MIIS); -nnid.use('/v1/api/oauth20', routes.OAUTH); -nnid.use('/v1/api/people', routes.PEOPLE); -nnid.use('/v1/api/provider', routes.PROVIDER); -nnid.use('/v1/api/support', routes.SUPPORT); - -// Main router for endpoints -const router = express.Router(); - -// Create subdomains -logger.info('[NNID] Creating \'account\' subdomain'); -router.use(subdomain('account', nnid)); - -logger.info('[NNID] Creating \'c.account\' subdomain'); -router.use(subdomain('c.account', nnid)); - -module.exports = router; \ No newline at end of file diff --git a/src/services/nnid/index.ts b/src/services/nnid/index.ts new file mode 100644 index 0000000..8fbcf6b --- /dev/null +++ b/src/services/nnid/index.ts @@ -0,0 +1,48 @@ +// handles "account.nintendo.net" endpoints + +import express from 'express'; +import subdomain from 'express-subdomain'; +import clientHeaderCheck from '@/middleware/client-header'; +import cemuMiddleware from '@/middleware/cemu'; +import pnidMiddleware from '@/middleware/pnid'; +import { LOG_INFO } from '@/logger'; + +import admin from '@/services/nnid/routes/admin'; +import content from '@/services/nnid/routes/content'; +import devices from '@/services/nnid/routes/devices'; +import miis from '@/services/nnid/routes/miis'; +import oauth from '@/services/nnid/routes/oauth'; +import people from '@/services/nnid/routes/people'; +import provider from '@/services/nnid/routes/provider'; +import support from '@/services/nnid/routes/support'; + +// Router to handle the subdomain restriction +const nnid = express.Router(); + +LOG_INFO('[NNID] Importing middleware'); +nnid.use(clientHeaderCheck); +nnid.use(cemuMiddleware); +nnid.use(pnidMiddleware); + +// Setup routes +LOG_INFO('[NNID] Applying imported routes'); +nnid.use('/v1/api/admin', admin); +nnid.use('/v1/api/content', content); +nnid.use('/v1/api/devices', devices); +nnid.use('/v1/api/miis', miis); +nnid.use('/v1/api/oauth20', oauth); +nnid.use('/v1/api/people', people); +nnid.use('/v1/api/provider', provider); +nnid.use('/v1/api/support', support); + +// Main router for endpoints +const router = express.Router(); + +// Create subdomains +LOG_INFO('[NNID] Creating \'account\' subdomain'); +router.use(subdomain('account', nnid)); + +LOG_INFO('[NNID] Creating \'c.account\' subdomain'); +router.use(subdomain('c.account', nnid)); + +export default router; \ No newline at end of file diff --git a/src/services/nnid/routes/admin.js b/src/services/nnid/routes/admin.js deleted file mode 100644 index be0b9f1..0000000 --- a/src/services/nnid/routes/admin.js +++ /dev/null @@ -1,59 +0,0 @@ -const router = require('express').Router(); -const xmlbuilder = require('xmlbuilder'); -const { PNID } = require('../../../models/pnid'); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/admin/mapped_ids - * Description: Maps between NNID usernames and PIDs - */ -router.get('/mapped_ids', async (request, response) => { - - let { input: inputList, input_type: inputType, output_type: outputType } = request.query; - inputList = inputList.split(','); - inputList = inputList.filter(input => input); // remove nulls - - if (inputType === 'user_id') { - inputType = 'usernameLower'; - inputList = inputList.map(name => name.toLowerCase()); - } - - if (outputType === 'user_id') { - outputType = 'username'; - } - - // This is slower than PNID.where() - // but it ensures that each input - // ALWAYS has an output and filters - // out unwanted input/output types - const results = []; - const allowedTypes = ['pid', 'user_id']; - - for (const input of inputList) { - const result = { - in_id: input, - out_id: '' - }; - - if (allowedTypes.includes(request.query.input_type) && allowedTypes.includes(request.query.output_type)) { - const query = {}; - query[inputType] = input; - - const searchResult = await PNID.findOne(query); - - if (searchResult) { - result.out_id = searchResult.get(outputType); - } - } - - results.push(result); - } - - response.send(xmlbuilder.create({ - mapped_ids: { - mapped_id: results - } - }).end()); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/nnid/routes/admin.ts b/src/services/nnid/routes/admin.ts new file mode 100644 index 0000000..f1b63b4 --- /dev/null +++ b/src/services/nnid/routes/admin.ts @@ -0,0 +1,106 @@ +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import { getValueFromQueryString } from '@/util'; +import { PNID } from '@/models/pnid'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +const router: express.Router = express.Router(); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/admin/mapped_ids + * Description: Maps between NNID usernames and PIDs + */ +router.get('/mapped_ids', async (request: express.Request, response: express.Response) => { + const inputType: string | undefined = getValueFromQueryString(request.query, 'input_type'); + const outputType: string | undefined = getValueFromQueryString(request.query, 'output_type'); + const input: string | undefined = getValueFromQueryString(request.query, 'input'); + + if (!inputType || !outputType || !input) { + return response.status(400).send(xmlbuilder.create({ + errors: { + error: { + cause: 'Bad Request', + code: '1600', + message: 'Unable to process request' + } + } + }).end()); + } + + let inputList: string[] = input.split(','); + let queryInput: string; + let queryOutput: string; + + inputList = inputList.filter(input => input); // * Remove null inputs + + if (inputType === 'user_id') { + queryInput = 'usernameLower'; + inputList = inputList.map(name => name.toLowerCase()); + } else { + queryInput = 'pid'; + } + + if (outputType === 'user_id') { + queryOutput = 'username'; + } else { + queryOutput = 'pid'; + } + + // * This is slower than PNID.where() + // * but it ensures that each input + // * ALWAYS has an output and filters + // * out unwanted input/output types + const results: { + in_id: string; + out_id: string; + }[] = []; + const allowedTypes: string[] = ['pid', 'user_id']; + + for (const input of inputList) { + const result: { + in_id: string; + out_id: string; + } = { + in_id: input, + out_id: '' + }; + + if (allowedTypes.includes(inputType) && allowedTypes.includes(outputType)) { + const query: { + usernameLower?: string; + pid?: number; + } = {}; + + if (queryInput === 'usernameLower') { + query.usernameLower = input; + } + + if (queryInput === 'pid') { + query.pid = Number(input); + + if (isNaN(query.pid)) { + // * Bail early + results.push(result); + continue; + } + } + + const searchResult: HydratedPNIDDocument | null = await PNID.findOne(query); + + if (searchResult) { + result.out_id = searchResult.get(queryOutput); + } + } + + results.push(result); + } + + response.send(xmlbuilder.create({ + mapped_ids: { + mapped_id: results + } + }).end()); +}); + +export default router; \ No newline at end of file diff --git a/src/services/nnid/routes/content.js b/src/services/nnid/routes/content.ts similarity index 81% rename from src/services/nnid/routes/content.js rename to src/services/nnid/routes/content.ts index e8b9d70..56dc2f5 100644 --- a/src/services/nnid/routes/content.js +++ b/src/services/nnid/routes/content.ts @@ -1,16 +1,20 @@ -const router = require('express').Router(); -const xmlbuilder = require('xmlbuilder'); -const timezones = require('../timezones.json'); +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import timezones from '@/services/nnid/timezones.json'; +import { RegionLanguages } from '@/types/services/nnid/region-languages'; +import { RegionTimezones } from '@/types/services/nnid/region-timezones'; + +const router: express.Router = express.Router(); /** * [GET] * Replacement for: https://account.nintendo.net/v1/api/content/agreements/TYPE/REGION/VERSION * Description: Sends the client requested agreement */ -router.get('/agreements/:type/:region/:version', (request, response) => { +router.get('/agreements/:type/:region/:version', (request: express.Request, response: express.Response) => { response.set('Content-Type', 'text/xml'); response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', new Date().getTime()); + response.set('X-Nintendo-Date', new Date().getTime().toString()); response.send(xmlbuilder.create({ agreements: { @@ -124,10 +128,10 @@ router.get('/agreements/:type/:region/:version', (request, response) => { * Replacement for: https://account.nintendo.net/v1/api/content/time_zones/COUNTRY/LANGUAGE * Description: Sends the client the requested timezones */ -router.get('/time_zones/:countryCode/:language', (request, response) => { +router.get('/time_zones/:countryCode/:language', (request: express.Request, response: express.Response) => { response.set('Content-Type', 'text/xml'); response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', new Date().getTime()); + response.set('X-Nintendo-Date', new Date().getTime().toString()); /* // Old method. Crashes WiiU when sending a list with over 32 entries, but otherwise works @@ -147,10 +151,11 @@ router.get('/time_zones/:countryCode/:language', (request, response) => { }); */ - const { countryCode, language } = request.params; + const countryCode: string = request.params.countryCode; + const language: string = request.params.language; - const regionLanguages = timezones[countryCode]; - const regionTimezones = regionLanguages[language] ? regionLanguages[language] : Object.values(regionLanguages)[0]; + const regionLanguages: RegionLanguages = timezones[countryCode as keyof typeof timezones]; + const regionTimezones: RegionTimezones = regionLanguages[language] ? regionLanguages[language] : Object.values(regionLanguages)[0]; response.send(xmlbuilder.create({ timezones: { @@ -159,4 +164,4 @@ router.get('/time_zones/:countryCode/:language', (request, response) => { }).end()); }); -module.exports = router; +export default router; \ No newline at end of file diff --git a/src/services/nnid/routes/devices.js b/src/services/nnid/routes/devices.js deleted file mode 100644 index 4dd3e4e..0000000 --- a/src/services/nnid/routes/devices.js +++ /dev/null @@ -1,15 +0,0 @@ -const router = require('express').Router(); -const xmlbuilder = require('xmlbuilder'); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/devices/@current/status - * Description: Unknown use - */ -router.get('/@current/status', async (request, response) => { - response.send(xmlbuilder.create({ - device: '' - }).end()); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/nnid/routes/devices.ts b/src/services/nnid/routes/devices.ts new file mode 100644 index 0000000..3626fb1 --- /dev/null +++ b/src/services/nnid/routes/devices.ts @@ -0,0 +1,18 @@ +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; + +const router: express.Router = express.Router(); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/devices/@current/status + * Description: Unknown use + */ +router.get('/@current/status', async (request: express.Request, response: express.Response) => { + // TODO - Finish this + response.send(xmlbuilder.create({ + device: '' + }).end()); +}); + +export default router; \ No newline at end of file diff --git a/src/services/nnid/routes/index.js b/src/services/nnid/routes/index.js deleted file mode 100644 index b5becf4..0000000 --- a/src/services/nnid/routes/index.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - ADMIN: require('./admin'), - CONTENT: require('./content'), - DEVICES: require('./devices'), - MIIS: require('./miis'), - OAUTH: require('./oauth'), - PEOPLE: require('./people'), - PROVIDER: require('./provider'), - SUPPORT: require('./support'), -}; \ No newline at end of file diff --git a/src/services/nnid/routes/miis.js b/src/services/nnid/routes/miis.js deleted file mode 100644 index 3c5603c..0000000 --- a/src/services/nnid/routes/miis.js +++ /dev/null @@ -1,94 +0,0 @@ -const router = require('express').Router(); -const xmlbuilder = require('xmlbuilder'); -const { PNID } = require('../../../models/pnid'); -const { config } = require('../../../config-manager'); - -/** - * [GET] - * Replacement for: https://account.pretendo.cc/v1/api/miis - * Description: Returns a list of NNID miis - */ -router.get('/', async (request, response) => { - - const { pids } = request.query; - - const results = await PNID.where('pid', pids); - const miis = []; - - for (const user of results) { - const { mii } = user; - - const miiImages = [ - { - cached_url: `${config.cdn.base_url}/mii/${user.pid}/normal_face.png`, - id: mii.id, - url: `${config.cdn.base_url}/mii/${user.pid}/normal_face.png`, - type: 'standard' - }, - { - cached_url: `${config.cdn.base_url}/mii/${user.pid}/frustrated.png`, - id: mii.id, - url: `${config.cdn.base_url}/mii/${user.pid}/frustrated.png`, - type: 'frustrated_face' - }, - { - cached_url: `${config.cdn.base_url}/mii/${user.pid}/smile_open_mouth.png`, - id: mii.id, - url: `${config.cdn.base_url}/mii/${user.pid}/smile_open_mouth.png`, - type: 'happy_face' - }, - { - cached_url: `${config.cdn.base_url}/mii/${user.pid}/wink_left.png`, - id: mii.id, - url: `${config.cdn.base_url}/mii/${user.pid}/wink_left.png`, - type: 'like_face' - }, - { - cached_url: `${config.cdn.base_url}/mii/${user.pid}/normal_face.png`, - id: mii.id, - url: `${config.cdn.base_url}/mii/${user.pid}/normal_face.png`, - type: 'normal_face' - }, - { - cached_url: `${config.cdn.base_url}/mii/${user.pid}/sorrow.png`, - id: mii.id, - url: `${config.cdn.base_url}/mii/${user.pid}/sorrow.png`, - type: 'puzzled_face' - }, - { - cached_url: `${config.cdn.base_url}/mii/${user.pid}/surprised_open_mouth.png`, - id: mii.id, - url: `${config.cdn.base_url}/mii/${user.pid}/surprised_open_mouth.png`, - type: 'surprised_face' - }, - { - cached_url: `${config.cdn.base_url}/mii/${user.pid}/body.png`, - id: mii.id, - url: `${config.cdn.base_url}/mii/${user.pid}/body.png`, - type: 'whole_body' - } - ]; - - miis.push({ - data: mii.data.replace(/(\r\n|\n|\r)/gm, ''), - id: mii.id, - images: { - image: miiImages - }, - name: mii.name, - pid: user.pid, - primary: mii.primary ? 'Y' : 'N', - user_id: user.username - }); - } - - //console.log(results[0].mii.data.replace(/(\r\n|\n|\r)/gm, '')); - - response.send(xmlbuilder.create({ - miis: { - mii: miis - } - }).end()); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/nnid/routes/miis.ts b/src/services/nnid/routes/miis.ts new file mode 100644 index 0000000..dd58978 --- /dev/null +++ b/src/services/nnid/routes/miis.ts @@ -0,0 +1,131 @@ +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import { getValueFromQueryString } from '@/util'; +import { PNID } from '@/models/pnid'; +import { config } from '@/config-manager'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; +import { YesNoBoolString } from '@/types/common/yes-no-bool-string'; + +const router: express.Router = express.Router(); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/miis + * Description: Returns a list of NNID miis + */ +router.get('/', async (request: express.Request, response: express.Response) => { + const input: string | undefined = getValueFromQueryString(request.query, 'pids'); + + if (!input) { + return response.status(400).send(xmlbuilder.create({ + errors: { + error: { + cause: 'Bad Request', + code: '1600', + message: 'Unable to process request' + } + } + }).end()); + } + + const pids: number[] = input.split(',').map(pid => Number(pid)); + + const results: HydratedPNIDDocument[] = await PNID.where('pid', pids); + const miis: { + data: string; + id: number; + images: { + image: { + cached_url: string; + id: number; + url: string; + type: string; + }[] + }; + name: string; + pid: number; + primary: YesNoBoolString; + user_id: string; + }[] = []; + + for (const user of results) { + const mii: { + name: string; + primary: boolean; + data: string; + id: number; + hash: string; + image_url: string; + image_id: number; + } = user.mii; + + miis.push({ + data: mii.data.replace(/(\r\n|\n|\r)/gm, ''), + id: mii.id, + images: { + image: [ + { + cached_url: `${config.cdn.base_url}/mii/${user.pid}/normal_face.png`, + id: mii.id, + url: `${config.cdn.base_url}/mii/${user.pid}/normal_face.png`, + type: 'standard' + }, + { + cached_url: `${config.cdn.base_url}/mii/${user.pid}/frustrated.png`, + id: mii.id, + url: `${config.cdn.base_url}/mii/${user.pid}/frustrated.png`, + type: 'frustrated_face' + }, + { + cached_url: `${config.cdn.base_url}/mii/${user.pid}/smile_open_mouth.png`, + id: mii.id, + url: `${config.cdn.base_url}/mii/${user.pid}/smile_open_mouth.png`, + type: 'happy_face' + }, + { + cached_url: `${config.cdn.base_url}/mii/${user.pid}/wink_left.png`, + id: mii.id, + url: `${config.cdn.base_url}/mii/${user.pid}/wink_left.png`, + type: 'like_face' + }, + { + cached_url: `${config.cdn.base_url}/mii/${user.pid}/normal_face.png`, + id: mii.id, + url: `${config.cdn.base_url}/mii/${user.pid}/normal_face.png`, + type: 'normal_face' + }, + { + cached_url: `${config.cdn.base_url}/mii/${user.pid}/sorrow.png`, + id: mii.id, + url: `${config.cdn.base_url}/mii/${user.pid}/sorrow.png`, + type: 'puzzled_face' + }, + { + cached_url: `${config.cdn.base_url}/mii/${user.pid}/surprised_open_mouth.png`, + id: mii.id, + url: `${config.cdn.base_url}/mii/${user.pid}/surprised_open_mouth.png`, + type: 'surprised_face' + }, + { + cached_url: `${config.cdn.base_url}/mii/${user.pid}/body.png`, + id: mii.id, + url: `${config.cdn.base_url}/mii/${user.pid}/body.png`, + type: 'whole_body' + } + ] + }, + name: mii.name, + pid: user.pid, + primary: mii.primary ? 'Y' : 'N', + user_id: user.username + }); + } + + response.send(xmlbuilder.create({ + miis: { + mii: miis + } + }).end()); +}); + +export default router; \ No newline at end of file diff --git a/src/services/nnid/routes/oauth.js b/src/services/nnid/routes/oauth.js deleted file mode 100644 index 21516ad..0000000 --- a/src/services/nnid/routes/oauth.js +++ /dev/null @@ -1,120 +0,0 @@ -const router = require('express').Router(); -const xmlbuilder = require('xmlbuilder'); -const bcrypt = require('bcrypt'); -const fs = require('fs-extra'); -const database = require('../../../database'); -const util = require('../../../util'); - -/** - * [POST] - * Replacement for: https://account.nintendo.net/v1/api/oauth20/access_token/generate - * Description: Generates an access token for a user - */ -router.post('/access_token/generate', async (request, response) => { - const { body } = request; - const { grant_type, user_id, password } = body; - - if (!['password', 'refresh_token'].includes(grant_type)) { - response.status(400); - return response.send(xmlbuilder.create({ - error: { - cause: 'grant_type', - code: '0004', - message: 'Invalid Grant Type' - } - }).end()); - } - - if (!user_id || user_id.trim() === '') { - response.status(400); - return response.send(xmlbuilder.create({ - error: { - cause: 'user_id', - code: '0002', - message: 'user_id format is invalid' - } - }).end()); - } - - if (!password || password.trim() === '') { - response.status(400); - return response.send(xmlbuilder.create({ - error: { - cause: 'password', - code: '0002', - message: 'password format is invalid' - } - }).end()); - } - - const pnid = await database.getUserByUsername(user_id); - - if (!pnid || !await bcrypt.compare(password, pnid.password)) { - response.status(400); - return response.send(xmlbuilder.create({ - error: { - code: '0106', - message: 'Invalid account ID or password' - } - }).end({ pretty: true })); - } - - if (pnid.get('access_level') < 0) { - return response.status(400).send(xmlbuilder.create({ - errors: { - error: { - code: '0122', - message: 'Device has been banned by game server' - } - } - }).end()); - } - - const cryptoPath = `${__dirname}/../../../../certs/service/account`; - - if (!await fs.pathExists(cryptoPath)) { - // Need to generate keys - return response.send(xmlbuilder.create({ - errors: { - error: { - code: '0000', - message: 'Could not find account access key crypto path' - } - } - }).end()); - } - - const accessTokenOptions = { - system_type: 0x1, // WiiU - token_type: 0x1, // OAuth Access, - pid: pnid.get('pid'), - expire_time: BigInt(Date.now() + (3600 * 1000)) - }; - - const refreshTokenOptions = { - system_type: 0x1, // WiiU - token_type: 0x2, // OAuth Refresh, - pid: pnid.get('pid'), - expire_time: BigInt(Date.now() + (3600 * 1000)) - }; - - let accessToken = await util.generateToken(null, accessTokenOptions); - let refreshToken = await util.generateToken(null, refreshTokenOptions); - - if (request.isCemu) { - accessToken = Buffer.from(accessToken, 'base64').toString('hex'); - refreshToken = Buffer.from(refreshToken, 'base64').toString('hex'); - } - - response.send(xmlbuilder.create({ - OAuth20: { - access_token: { - token: accessToken, - refresh_token: refreshToken, - expires_in: 3600 - } - } - }).end()); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/nnid/routes/oauth.ts b/src/services/nnid/routes/oauth.ts new file mode 100644 index 0000000..c276b9f --- /dev/null +++ b/src/services/nnid/routes/oauth.ts @@ -0,0 +1,111 @@ +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import bcrypt from 'bcrypt'; +import { getPNIDByUsername } from '@/database'; +import { generateToken } from '@/util'; +import { config } from '@/config-manager'; +import { TokenOptions } from '@/types/common/token-options'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +const router: express.Router = express.Router(); + +/** + * [POST] + * Replacement for: https://account.nintendo.net/v1/api/oauth20/access_token/generate + * Description: Generates an access token for a user + */ +router.post('/access_token/generate', async (request: express.Request, response: express.Response) => { + const grantType: string = request.body?.grant_type; + const username: string = request.body?.user_id; + const password: string = request.body?.password; + + if (!['password', 'refresh_token'].includes(grantType)) { + response.status(400); + return response.send(xmlbuilder.create({ + error: { + cause: 'grant_type', + code: '0004', + message: 'Invalid Grant Type' + } + }).end()); + } + + if (!username || username.trim() === '') { + response.status(400); + return response.send(xmlbuilder.create({ + error: { + cause: 'user_id', + code: '0002', + message: 'user_id format is invalid' + } + }).end()); + } + + if (!password || password.trim() === '') { + response.status(400); + return response.send(xmlbuilder.create({ + error: { + cause: 'password', + code: '0002', + message: 'password format is invalid' + } + }).end()); + } + + const pnid: HydratedPNIDDocument | null = await getPNIDByUsername(username); + + if (!pnid || !await bcrypt.compare(password, pnid.password)) { + response.status(400); + return response.send(xmlbuilder.create({ + error: { + code: '0106', + message: 'Invalid account ID or password' + } + }).end({ pretty: true })); + } + + if (pnid.access_level < 0) { + return response.status(400).send(xmlbuilder.create({ + errors: { + error: { + code: '0122', + message: 'Device has been banned by game server' + } + } + }).end()); + } + + const accessTokenOptions: TokenOptions = { + system_type: 0x1, // * WiiU + token_type: 0x1, // * OAuth Access + pid: pnid.pid, + expire_time: BigInt(Date.now() + (3600 * 1000)) + }; + + const refreshTokenOptions: TokenOptions = { + system_type: 0x1, // * WiiU + token_type: 0x2, // * OAuth Refresh + pid: pnid.pid, + expire_time: BigInt(Date.now() + (3600 * 1000)) + }; + + const accessTokenBuffer: Buffer | null = await generateToken(config.aes_key, accessTokenOptions); + const refreshTokenBuffer: Buffer | null = await generateToken(config.aes_key, refreshTokenOptions); + + const accessToken: string = accessTokenBuffer ? accessTokenBuffer.toString('hex') : ''; + const refreshToken: string = refreshTokenBuffer ? refreshTokenBuffer.toString('hex') : ''; + + // TODO - Handle null tokens + + response.send(xmlbuilder.create({ + OAuth20: { + access_token: { + token: accessToken, + refresh_token: refreshToken, + expires_in: 3600 + } + } + }).end()); +}); + +export default router; \ No newline at end of file diff --git a/src/services/nnid/routes/people.js b/src/services/nnid/routes/people.js deleted file mode 100644 index 7d1287a..0000000 --- a/src/services/nnid/routes/people.js +++ /dev/null @@ -1,496 +0,0 @@ -const router = require('express').Router(); -const xmlbuilder = require('xmlbuilder'); -const moment = require('moment'); -const crypto = require('crypto'); -const bcrypt = require('bcrypt'); -const { PNID } = require('../../../models/pnid'); -const { NEXAccount } = require('../../../models/nex-account'); -const deviceCertificateMiddleware = require('../../../middleware/device-certificate'); -const ratelimit = require('../../../middleware/ratelimit'); -const database = require('../../../database'); -const util = require('../../../util'); -const logger = require('../../../../logger'); -require('moment-timezone'); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/people/:USERNAME - * Description: Checks if a username is in use - */ -router.get('/:username', async (request, response) => { - const { username } = request.params; - - const userExists = await database.doesUserExist(username); - - if (userExists) { - response.status(400); - - return response.end(xmlbuilder.create({ - errors: { - error: { - code: '0100', - message: 'Account ID already exists' - } - } - }).end()); - } - - response.status(200); - response.end(); -}); - -/** - * [POST] - * Replacement for: https://account.nintendo.net/v1/api/people - * Description: Registers a new NNID - */ -router.post('/', ratelimit, deviceCertificateMiddleware, async (request, response) => { - if (!request.certificate.valid) { - // TODO: Change this to a different error - response.status(400); - - return response.send(xmlbuilder.create({ - error: { - cause: 'Bad Request', - code: '1600', - message: 'Unable to process request' - } - }).end()); - } - - const person = request.body.get('person'); - - const userExists = await database.doesUserExist(person.get('user_id')); - - if (userExists) { - response.status(400); - - return response.end(xmlbuilder.create({ - errors: { - error: { - code: '0100', - message: 'Account ID already exists' - } - } - }).end()); - } - - const creationDate = moment().format('YYYY-MM-DDTHH:MM:SS'); - let pnid; - let nexAccount; - - const session = await database.connection.startSession(); - await session.startTransaction(); - - try { - nexAccount = new NEXAccount({ - device_type: 'wiiu', - }); - - await nexAccount.generatePID(); - await nexAccount.generatePassword(); - - // Quick hack to get the PIDs to match - // TODO: Change this maybe? - // NN with a NNID will always use the NNID PID - // even if the provided NEX PID is different - // To fix this we make them the same PID - nexAccount.owning_pid = nexAccount.get('pid'); - - await nexAccount.save({ session }); - - const primaryPasswordHash = util.nintendoPasswordHash(person.get('password'), nexAccount.get('pid')); - const passwordHash = await bcrypt.hash(primaryPasswordHash, 10); - - pnid = new PNID({ - pid: nexAccount.get('pid'), - creation_date: creationDate, - updated: creationDate, - username: person.get('user_id'), - usernameLower: person.get('user_id').toLowerCase(), - password: passwordHash, - birthdate: person.get('birth_date'), - gender: person.get('gender'), - country: person.get('country'), - language: person.get('language'), - email: { - address: person.get('email').get('address').toLowerCase(), - primary: person.get('email').get('primary') === 'Y', - parent: person.get('email').get('parent') === 'Y', - reachable: false, - validated: person.get('email').get('validated') === 'Y', - id: crypto.randomBytes(4).readUInt32LE() - }, - region: person.get('region'), - timezone: { - name: person.get('tz_name'), - offset: (moment.tz(person.get('tz_name')).utcOffset() * 60) - }, - mii: { - name: person.get('mii').get('name'), - primary: person.get('mii').get('name') === 'Y', - data: person.get('mii').get('data'), - id: crypto.randomBytes(4).readUInt32LE(), - hash: crypto.randomBytes(7).toString('hex'), - image_url: '', // deprecated, will be removed in the future - image_id: crypto.randomBytes(4).readUInt32LE() - }, - flags: { - active: true, - marketing: person.get('marketing_flag') === 'Y', - off_device: person.get('off_device_flag') === 'Y' - }, - identification: { - email_code: 1, // will be overwritten before saving - email_token: '' // will be overwritten before saving - } - }); - - await pnid.generateEmailValidationCode(); - await pnid.generateEmailValidationToken(); - await pnid.generateMiiImages(); - - await pnid.save({ session }); - - await session.commitTransaction(); - } catch (error) { - logger.error('[POST] /v1/api/people: ' + error); - - await session.abortTransaction(); - - response.status(400); - - return response.send(xmlbuilder.create({ - error: { - cause: 'Bad Request', - code: '1600', - message: 'Unable to process request' - } - }).end()); - } finally { - // * This runs regardless of failure - // * Returning on catch will not prevent this from running - await session.endSession(); - } - - await util.sendConfirmationEmail(pnid); - - response.send(xmlbuilder.create({ - person: { - pid: pnid.get('pid') - } - }).end()); -}); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/profile - * Description: Gets a users profile - */ -router.get('/@me/profile', async (request, response) => { - response.set('Content-Type', 'text/xml'); - response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', new Date().getTime()); - - const { pnid } = request; - - const person = await database.getUserProfileJSONByPID(pnid.get('pid')); - - response.send(xmlbuilder.create({ - person - }, { separateArrayItems: true }).end()); -}); - -/** - * [POST] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices - * Description: Gets user profile, seems to be the same as https://account.nintendo.net/v1/api/people/@me/profile - */ -router.post('/@me/devices', async (request, response) => { - response.set('Content-Type', 'text/xml'); - response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', new Date().getTime()); - - // We don't care about the device attributes - // The console ignores them and PNIDs are not tied to consoles anyway - // So the server also ignores them and does not save the ones posted here - - const { pnid } = request; - - const person = await database.getUserProfileJSONByPID(pnid.get('pid')); - - response.send(xmlbuilder.create({ - person - }).end()); -}); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices - * Description: Returns only user devices - */ -router.get('/@me/devices', async (request, response) => { - response.set('Content-Type', 'text/xml'); - response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', new Date().getTime()); - - const { pnid, headers } = request; - - response.send(xmlbuilder.create({ - devices: [ - { - device: { - device_id: headers['x-nintendo-device-id'], - language: headers['accept-language'], - updated: moment().format('YYYY-MM-DDTHH:MM:SS'), - pid: pnid.get('pid'), - platform_id: headers['x-nintendo-platform-id'], - region: headers['x-nintendo-region'], - serial_number: headers['x-nintendo-serial-number'], - status: 'ACTIVE', - system_version: headers['x-nintendo-system-version'], - type: 'RETAIL', - updated_by: 'USER' - } - } - ] - }).end()); -}); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices/owner - * Description: Gets user profile, seems to be the same as https://account.nintendo.net/v1/api/people/@me/profile - */ -router.get('/@me/devices/owner', async (request, response) => { - response.set('Content-Type', 'text/xml'); - response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', moment().add(5, 'h')); - - const { pnid } = request; - - const person = await database.getUserProfileJSONByPID(pnid.get('pid')); - - response.send(xmlbuilder.create({ - person - }).end()); -}); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices/status - * Description: Unknown use - */ -router.get('/@me/devices/status', async (request, response) => { - response.set('Content-Type', 'text/xml'); - response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', moment().add(5, 'h')); - - response.send(xmlbuilder.create({ - device: {} - }).end()); -}); - - -/** - * [PUT] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/miis/@primary - * Description: Updates a users Mii - */ -router.put('/@me/miis/@primary', async (request, response) => { - const { pnid } = request; - - const mii = request.body.get('mii'); - - const [name, primary, data] = [mii.get('name'), mii.get('primary'), mii.get('data')]; - - await pnid.updateMii({ name, primary, data }); - - response.send(''); -}); - -/** - * [PUT] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices/@current/inactivate - * Description: Deactivates a user from a console - */ -router.put('/@me/devices/@current/inactivate', async (request, response) => { - response.set('Server', 'Nintendo 3DS (http)'); - response.set('X-Nintendo-Date', new Date().getTime()); - - const { pnid } = request; - - if (!pnid) { - response.status(400); - - return response.end(xmlbuilder.create({ - errors: { - error: { - cause: 'access_token', - code: '0002', - message: 'Invalid access token' - } - } - }).end()); - } - - response.status(200); - response.end(); -}); - -/** - * [POST] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/deletion - * Description: Deletes a NNID - */ -router.put('/@me/deletion', async (request, response) => { - const { pnid } = request; - - if (!pnid) { - response.status(400); - - return response.end(xmlbuilder.create({ - errors: { - error: { - cause: 'access_token', - code: '0002', - message: 'Invalid access token' - } - } - }).end()); - } - - await PNID.deleteOne({ pid: pnid.get('pid') }); - - response.send(''); -}); - -/** - * [PUT] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/ - * Description: Updates a PNIDs account details - */ -router.put('/@me', async (request, response) => { - const { pnid } = request; - const person = request.body.get('person'); - - if (!pnid) { - response.status(400); - - return response.end(xmlbuilder.create({ - errors: { - error: { - cause: 'access_token', - code: '0002', - message: 'Invalid access token' - } - } - }).end()); - } - - const gender = person.get('gender') ? person.get('gender') : pnid.get('gender'); - const region = person.get('region') ? person.get('region') : pnid.get('region'); - const timezoneName = person.get('tz_name') ? person.get('tz_name') : pnid.get('timezone.name'); - const marketingFlag = person.get('marketing_flag') ? person.get('marketing_flag') === 'Y' : pnid.get('flags.marketing'); - const offDeviceFlag = person.get('off_device_flag') ? person.get('off_device_flag') === 'Y' : pnid.get('flags.off_device'); - - if (person.get('password')) { - const primaryPasswordHash = util.nintendoPasswordHash(person.get('password'), pnid.get('pid')); - const passwordHash = await bcrypt.hash(primaryPasswordHash, 10); - - pnid.password = passwordHash; - } - - pnid.gender = gender; - pnid.region = region; - pnid.timezone.name = timezoneName; - pnid.timezone.offset = (moment.tz(timezoneName).utcOffset() * 60); - pnid.timezone.marketing = marketingFlag; - pnid.timezone.off_device = offDeviceFlag; - - await pnid.save(); - - response.send(''); -}); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/emails/ - * Description: Gets a list (why?) of PNID emails - */ -router.get('/@me/emails', async (request, response) => { - const { pnid } = request; - - if (!pnid) { - response.status(400); - - return response.end(xmlbuilder.create({ - errors: { - error: { - cause: 'access_token', - code: '0002', - message: 'Invalid access token' - } - } - }).end()); - } - - response.send(xmlbuilder.create({ - emails: [ - { - email: { - address: pnid.get('email.address'), - id: pnid.get('email.id'), - parent: pnid.get('email.parent') ? 'Y' : 'N', - primary: pnid.get('email.primary') ? 'Y' : 'N', - reachable: pnid.get('email.reachable') ? 'Y' : 'N', - type: 'DEFAULT', // what is this? - updated_by: 'USER', // need to actually update this - validated: pnid.get('email.validated') ? 'Y' : 'N', - validated_date: pnid.get('email.validated_date'), - } - } - ] - }).end()); -}); - -/** - * [PUT] - * Replacement for: https://account.nintendo.net/v1/api/people/@me/emails/@primary - * Description: Updates a users email address - */ -router.put('/@me/emails/@primary', async (request, response) => { - const { pnid } = request; - const email = request.body.get('email'); - - if (!pnid) { - response.status(400); - - return response.end(xmlbuilder.create({ - errors: { - error: { - cause: 'access_token', - code: '0002', - message: 'Invalid access token' - } - } - }).end()); - } - - pnid.set('email.address', email.get('address').toLowerCase()); - pnid.set('email.reachable', false); - pnid.set('email.validated', false); - pnid.set('email.validated_date', ''); - pnid.set('email.id', crypto.randomBytes(4).readUInt32LE()); - - await pnid.generateEmailValidationCode(); - await pnid.generateEmailValidationToken(); - - await pnid.save(); - - await util.sendConfirmationEmail(pnid); - - response.send(''); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/nnid/routes/people.ts b/src/services/nnid/routes/people.ts new file mode 100644 index 0000000..a1c271e --- /dev/null +++ b/src/services/nnid/routes/people.ts @@ -0,0 +1,678 @@ +import crypto from 'node:crypto'; +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import bcrypt from 'bcrypt'; +import moment from 'moment'; +import mongoose from 'mongoose'; +import deviceCertificateMiddleware from '@/middleware/device-certificate'; +import ratelimit from '@/middleware/ratelimit'; +import { connection as databaseConnection, doesPNIDExist, getPNIDProfileJSONByPID } from '@/database'; +import { getValueFromHeaders, nintendoPasswordHash, sendConfirmationEmail } from '@/util'; +import { PNID } from '@/models/pnid'; +import { NEXAccount } from '@/models/nex-account'; +import { LOG_ERROR } from '@/logger'; + +import timezones from '@/services/nnid/timezones.json'; + +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; +import { HydratedNEXAccountDocument } from '@/types/mongoose/nex-account'; +import { RegionLanguages } from '@/types/services/nnid/region-languages'; +import { RegionTimezone, RegionTimezones } from '@/types/services/nnid/region-timezones'; +import { Person } from '@/types/services/nnid/person'; +import { PNIDProfile } from '@/types/services/nnid/pnid-profile'; + +const router: express.Router = express.Router(); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/people/:USERNAME + * Description: Checks if a username is in use + */ +router.get('/:username', async (request: express.Request, response: express.Response) => { + const username: string = request.params.username; + + const userExists: boolean = await doesPNIDExist(username); + + if (userExists) { + response.status(400); + + return response.end(xmlbuilder.create({ + errors: { + error: { + code: '0100', + message: 'Account ID already exists' + } + } + }).end()); + } + + response.status(200); + response.end(); +}); + +/** + * [POST] + * Replacement for: https://account.nintendo.net/v1/api/people + * Description: Registers a new NNID + */ +router.post('/', ratelimit, deviceCertificateMiddleware, async (request: express.Request, response: express.Response) => { + if (!request.certificate || !request.certificate.valid) { + // TODO: Change this to a different error + response.status(400); + + return response.send(xmlbuilder.create({ + error: { + cause: 'Bad Request', + code: '1600', + message: 'Unable to process request' + } + }).end()); + } + + const person: Person = request.body.person; + + const userExists: boolean = await doesPNIDExist(person.user_id); + + if (userExists) { + response.status(400); + + return response.end(xmlbuilder.create({ + errors: { + error: { + code: '0100', + message: 'Account ID already exists' + } + } + }).end()); + } + + const creationDate: string = moment().format('YYYY-MM-DDTHH:MM:SS'); + let pnid: HydratedPNIDDocument; + let nexAccount: HydratedNEXAccountDocument; + + const session: mongoose.ClientSession = await databaseConnection().startSession(); + await session.startTransaction(); + + try { + nexAccount = new NEXAccount({ + device_type: 'wiiu', + }); + + await nexAccount.generatePID(); + await nexAccount.generatePassword(); + + // Quick hack to get the PIDs to match + // TODO: Change this maybe? + // NN with a NNID will always use the NNID PID + // even if the provided NEX PID is different + // To fix this we make them the same PID + nexAccount.owning_pid = nexAccount.pid; + + await nexAccount.save({ session }); + + const primaryPasswordHash: string = nintendoPasswordHash(person.password, nexAccount.pid); + const passwordHash: string = await bcrypt.hash(primaryPasswordHash, 10); + + const countryCode: string = person.country; + const language: string = person.language; + const timezoneName: string = person.tz_name; + + const regionLanguages: RegionLanguages = timezones[countryCode as keyof typeof timezones]; + const regionTimezones: RegionTimezones = regionLanguages[language] ? regionLanguages[language] : Object.values(regionLanguages)[0]; + let timezone: RegionTimezone | undefined = regionTimezones.find(tz => tz.area === timezoneName); + + if (!timezone) { + // TODO - Change this, handle the error + timezone = { + area: 'America/New_York', + language: 'en', + name: 'Eastern Time (US & Canada)', + order: '11', + utc_offset: '-14400' + }; + } + + pnid = new PNID({ + pid: nexAccount.pid, + creation_date: creationDate, + updated: creationDate, + username: person.user_id, + usernameLower: person.user_id.toLowerCase(), + password: passwordHash, + birthdate: person.birth_date, + gender: person.gender, + country: countryCode, + language: language, + email: { + address: person.email.address.toLowerCase(), + primary: person.email.primary === 'Y', + parent: person.email.parent === 'Y', + reachable: false, + validated: person.email.validated === 'Y', + id: crypto.randomBytes(4).readUInt32LE() + }, + region: person.region, + timezone: { + name: timezoneName, + offset: Number(timezone.utc_offset) + }, + mii: { + name: person.mii.name, + primary: person.mii.name === 'Y', + data: person.mii.data, + id: crypto.randomBytes(4).readUInt32LE(), + hash: crypto.randomBytes(7).toString('hex'), + image_url: '', // deprecated, will be removed in the future + image_id: crypto.randomBytes(4).readUInt32LE() + }, + flags: { + active: true, + marketing: person.marketing_flag === 'Y', + off_device: person.off_device_flag === 'Y' + }, + identification: { + email_code: 1, // will be overwritten before saving + email_token: '' // will be overwritten before saving + } + }); + + await pnid.generateEmailValidationCode(); + await pnid.generateEmailValidationToken(); + await pnid.generateMiiImages(); + + await pnid.save({ session }); + + await session.commitTransaction(); + } catch (error) { + LOG_ERROR('[POST] /v1/api/people: ' + error); + + await session.abortTransaction(); + + response.status(400); + + return response.send(xmlbuilder.create({ + error: { + cause: 'Bad Request', + code: '1600', + message: 'Unable to process request' + } + }).end()); + } finally { + // * This runs regardless of failure + // * Returning on catch will not prevent this from running + await session.endSession(); + } + + await sendConfirmationEmail(pnid); + + response.send(xmlbuilder.create({ + person: { + pid: pnid.pid + } + }).end()); +}); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/profile + * Description: Gets a users profile + */ +router.get('/@me/profile', async (request: express.Request, response: express.Response) => { + response.set('Content-Type', 'text/xml'); + response.set('Server', 'Nintendo 3DS (http)'); + response.set('X-Nintendo-Date', new Date().getTime().toString()); + + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + // TODO - Research this error more + return response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); + } + + const person: PNIDProfile | null = await getPNIDProfileJSONByPID(pnid.pid); + + if (!person) { + // TODO - Research this error more + return response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); + } + + response.send(xmlbuilder.create({ + person + }, { separateArrayItems: true }).end()); +}); + +/** + * [POST] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices + * Description: Gets user profile, seems to be the same as https://account.nintendo.net/v1/api/people/@me/profile + */ +router.post('/@me/devices', async (request: express.Request, response: express.Response) => { + response.set('Content-Type', 'text/xml'); + response.set('Server', 'Nintendo 3DS (http)'); + response.set('X-Nintendo-Date', new Date().getTime().toString()); + + // We don't care about the device attributes + // The console ignores them and PNIDs are not tied to consoles anyway + // So the server also ignores them and does not save the ones posted here + + // TODO - CHANGE THIS. WE NEED TO SAVE CONSOLE DETAILS !!! + + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + // TODO - Research this error more + return response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); + } + + const person: PNIDProfile | null = await getPNIDProfileJSONByPID(pnid.pid); + + if (!person) { + // TODO - Research this error more + return response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); + } + + response.send(xmlbuilder.create({ + person + }).end()); +}); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices + * Description: Returns only user devices + */ +router.get('/@me/devices', async (request: express.Request, response: express.Response) => { + response.set('Content-Type', 'text/xml'); + response.set('Server', 'Nintendo 3DS (http)'); + response.set('X-Nintendo-Date', new Date().getTime().toString()); + + const pnid: HydratedPNIDDocument | null = request.pnid; + const deviceId: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-device-id'); + const acceptLanguage: string | undefined = getValueFromHeaders(request.headers, 'accept-language'); + const platformId: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-platform-id'); + const region: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-region'); + const serialNumber: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-serial-number'); + const systemVersion: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-system-version'); + + if (!deviceId || !acceptLanguage || !platformId || !region || !serialNumber || !systemVersion) { + // TODO - Research these error more + return response.status(400).send(xmlbuilder.create({ + errors: { + error: { + cause: 'Bad Request', + code: '1600', + message: 'Unable to process request' + } + } + }).end()); + } + + if (!pnid) { + // TODO - Research this error more + return response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); + } + + response.send(xmlbuilder.create({ + devices: [ + { + device: { + device_id: deviceId, + language: acceptLanguage, + updated: moment().format('YYYY-MM-DDTHH:MM:SS'), + pid: pnid.pid, + platform_id: platformId, + region: region, + serial_number: serialNumber, + status: 'ACTIVE', + system_version: systemVersion, + type: 'RETAIL', + updated_by: 'USER' + } + } + ] + }).end()); +}); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices/owner + * Description: Gets user profile, seems to be the same as https://account.nintendo.net/v1/api/people/@me/profile + */ +router.get('/@me/devices/owner', async (request: express.Request, response: express.Response) => { + response.set('Content-Type', 'text/xml'); + response.set('Server', 'Nintendo 3DS (http)'); + response.set('X-Nintendo-Date', moment().add(5, 'h').toString()); + + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + // TODO - Research this error more + return response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); + } + + const person: PNIDProfile | null = await getPNIDProfileJSONByPID(pnid.pid); + + if (!person) { + // TODO - Research this error more + return response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); + } + + response.send(xmlbuilder.create({ + person + }).end()); +}); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices/status + * Description: Unknown use + */ +router.get('/@me/devices/status', async (_request: express.Request, response: express.Response) => { + response.set('Content-Type', 'text/xml'); + response.set('Server', 'Nintendo 3DS (http)'); + response.set('X-Nintendo-Date', moment().add(5, 'h').toString()); + + response.send(xmlbuilder.create({ + device: {} + }).end()); +}); + + +/** + * [PUT] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/miis/@primary + * Description: Updates a users Mii + */ +router.put('/@me/miis/@primary', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + // TODO - Research this error more + return response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); + } + + const mii: { + name: string; + primary: string; + data: string; + } = request.body.mii; + + // TODO - Better checks + + const name: string = mii.name; + const primary: string = mii.primary; + const data: string = mii.data; + + await pnid.updateMii({ name, primary, data }); + + response.send(''); +}); + +/** + * [PUT] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/devices/@current/inactivate + * Description: Deactivates a user from a console + */ +router.put('/@me/devices/@current/inactivate', async (request: express.Request, response: express.Response) => { + response.set('Server', 'Nintendo 3DS (http)'); + response.set('X-Nintendo-Date', new Date().getTime().toString()); + + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + response.status(400); + + return response.end(xmlbuilder.create({ + errors: { + error: { + cause: 'access_token', + code: '0002', + message: 'Invalid access token' + } + } + }).end()); + } + + response.status(200); + response.end(); +}); + +/** + * [POST] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/deletion + * Description: Deletes a NNID + */ +router.put('/@me/deletion', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + response.status(400); + + return response.end(xmlbuilder.create({ + errors: { + error: { + cause: 'access_token', + code: '0002', + message: 'Invalid access token' + } + } + }).end()); + } + + response.send(''); +}); + +/** + * [PUT] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/ + * Description: Updates a PNIDs account details + */ +router.put('/@me', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + const person: Person = request.body.person; + + if (!pnid) { + response.status(400); + + return response.end(xmlbuilder.create({ + errors: { + error: { + cause: 'access_token', + code: '0002', + message: 'Invalid access token' + } + } + }).end()); + } + + const gender: string = person.gender ? person.gender : pnid.gender; + const region: number = person.region ? person.region : pnid.region; + const countryCode: string = person.country ? person.country : pnid.country; + const language: string = person.language ? person.language : pnid.language; + const timezoneName: string = person.tz_name ? person.tz_name : pnid.timezone.name; + const marketingFlag: boolean = person.marketing_flag ? person.marketing_flag === 'Y' : pnid.flags.marketing; + const offDeviceFlag: boolean = person.off_device_flag ? person.off_device_flag === 'Y' : pnid.flags.off_device; + + const regionLanguages: RegionLanguages = timezones[countryCode as keyof typeof timezones]; + const regionTimezones: RegionTimezones = regionLanguages[language] ? regionLanguages[language] : Object.values(regionLanguages)[0]; + let timezone: RegionTimezone | undefined = regionTimezones.find(tz => tz.area === timezoneName); + + if (!timezone) { + // TODO - Change this, handle the error + timezone = { + area: 'America/New_York', + language: 'en', + name: 'Eastern Time (US & Canada)', + order: '11', + utc_offset: '-14400' + }; + } + + if (person.password) { + const primaryPasswordHash: string = nintendoPasswordHash(person.password, pnid.pid); + const passwordHash: string = await bcrypt.hash(primaryPasswordHash, 10); + + pnid.password = passwordHash; + } + + pnid.gender = gender; + pnid.region = region; + pnid.timezone.name = timezoneName; + pnid.timezone.offset = Number(timezone.utc_offset); + pnid.timezone.marketing = marketingFlag; + pnid.timezone.off_device = offDeviceFlag; + + await pnid.save(); + + response.send(''); +}); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/emails/ + * Description: Gets a list (why?) of PNID emails + */ +router.get('/@me/emails', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + response.status(400); + + return response.end(xmlbuilder.create({ + errors: { + error: { + cause: 'access_token', + code: '0002', + message: 'Invalid access token' + } + } + }).end()); + } + + response.send(xmlbuilder.create({ + emails: [ + { + email: { + address: pnid.email.address, + id: pnid.email.id, + parent: pnid.email.parent ? 'Y' : 'N', + primary: pnid.email.primary ? 'Y' : 'N', + reachable: pnid.email.reachable ? 'Y' : 'N', + type: 'DEFAULT', // what is this? + updated_by: 'USER', // need to actually update this + validated: pnid.email.validated ? 'Y' : 'N', + validated_date: pnid.email.validated_date, + } + } + ] + }).end()); +}); + +/** + * [PUT] + * Replacement for: https://account.nintendo.net/v1/api/people/@me/emails/@primary + * Description: Updates a users email address + */ +router.put('/@me/emails/@primary', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + + const email: { + address: string; + } = request.body.email; + + if (!pnid || !email || !email.address) { + response.status(400); + + return response.end(xmlbuilder.create({ + errors: { + error: { + cause: 'access_token', + code: '0002', + message: 'Invalid access token' + } + } + }).end()); + } + + // TODO - Better email check + pnid.email.address = email.address.toLowerCase(); + pnid.email.reachable = false; + pnid.email.validated = false; + pnid.email.validated_date = ''; + pnid.email.id = crypto.randomBytes(4).readUInt32LE(); + + await pnid.generateEmailValidationCode(); + await pnid.generateEmailValidationToken(); + + await pnid.save(); + + await sendConfirmationEmail(pnid); + + response.send(''); +}); + +export default router; \ No newline at end of file diff --git a/src/services/nnid/routes/provider.js b/src/services/nnid/routes/provider.js deleted file mode 100644 index 86fd9d1..0000000 --- a/src/services/nnid/routes/provider.js +++ /dev/null @@ -1,172 +0,0 @@ -const router = require('express').Router(); -const xmlbuilder = require('xmlbuilder'); -const fs = require('fs-extra'); -const { NEXAccount } = require('../../../models/nex-account'); -const util = require('../../../util'); -const database = require('../../../database'); -const cache = require('../../../cache'); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/provider/service_token/@me - * Description: Gets a service token - */ -router.get('/service_token/@me', async (request, response) => { - const { pnid } = request; - - const titleId = request.headers['x-nintendo-title-id']; - const serverAccessLevel = pnid.get('server_access_level'); - const server = await database.getServerByTitleId(titleId, serverAccessLevel); - - if (!server) { - return response.send(xmlbuilder.create({ - errors: { - error: { - code: '1021', - message: 'The requested game server was not found' - } - } - }).end()); - } - - const { service_name, service_type, device } = server; - - const cryptoPath = `${__dirname}/../../../../certs/${service_type}/${service_name}`; - - if (!await fs.pathExists(cryptoPath)) { - // Need to generate keys - return response.send(xmlbuilder.create({ - errors: { - error: { - code: '1021', - message: 'The requested game server was not found' - } - } - }).end()); - } - - const publicKey = await cache.getServicePublicKey(service_name); - const secretKey = await cache.getServiceSecretKey(service_name); - - const cryptoOptions = { - public_key: publicKey, - hmac_secret: secretKey - }; - - const tokenOptions = { - system_type: device, - token_type: 0x4, // service token, - pid: pnid.get('pid'), - access_level: pnid.get('access_level'), - title_id: BigInt(parseInt(titleId, 16)), - expire_time: BigInt(Date.now() + (3600 * 1000)) - }; - - let serviceToken = await util.generateToken(cryptoOptions, tokenOptions); - - if (request.isCemu) { - serviceToken = Buffer.from(serviceToken, 'base64').toString('hex'); - } - - response.send(xmlbuilder.create({ - service_token: { - token: serviceToken - } - }).end()); -}); - -/** - * [GET] - * Replacement for: https://account.nintendo.net/v1/api/provider/nex_token/@me - * Description: Gets a NEX server address and token - */ -router.get('/nex_token/@me', async (request, response) => { - const { game_server_id: gameServerID } = request.query; - const { pnid } = request; - - if (!gameServerID) { - return response.send(xmlbuilder.create({ - errors: { - error: { - code: '0118', - message: 'Unique ID and Game Server ID are not linked' - } - } - }).end()); - } - - const serverAccessLevel = pnid.get('server_access_level'); - const server = await database.getServer(gameServerID, serverAccessLevel); - - if (!server) { - return response.send(xmlbuilder.create({ - errors: { - error: { - code: '1021', - message: 'The requested game server was not found' - } - } - }).end()); - } - - const { service_name, service_type, ip, port, device } = server; - const titleId = request.headers['x-nintendo-title-id']; - - const cryptoPath = `${__dirname}/../../../../certs/${service_type}/${service_name}`; - - if (!await fs.pathExists(cryptoPath)) { - // Need to generate keys - return response.send(xmlbuilder.create({ - errors: { - error: { - code: '1021', - message: 'The requested game server was not found' - } - } - }).end()); - } - - const publicKey = await cache.getNEXPublicKey(service_name); - const secretKey= await cache.getNEXSecretKey(service_name); - - const cryptoOptions = { - public_key: publicKey, - hmac_secret: secretKey - }; - - const tokenOptions = { - system_type: device, - token_type: 0x3, // nex token, - pid: pnid.get('pid'), - access_level: pnid.get('access_level'), - title_id: BigInt(parseInt(titleId, 16)), - expire_time: BigInt(Date.now() + (3600 * 1000)) - }; - - const nexUser = await NEXAccount.findOne({ - owning_pid: pnid.get('pid') - }); - - if (!nexUser) { - response.status(404); - return response.send('0008Not Found'); - } - - let nexToken = await util.generateToken(cryptoOptions, tokenOptions); - - if (request.isCemu) { - nexToken = Buffer.from(nexToken, 'base64').toString('hex'); - } - - response.send(xmlbuilder.create({ - nex_token: { - host: ip, - nex_password: nexUser.get('password'), - pid: nexUser.get('pid'), - port: port, - token: nexToken - } - }).end()); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/services/nnid/routes/provider.ts b/src/services/nnid/routes/provider.ts new file mode 100644 index 0000000..21160d3 --- /dev/null +++ b/src/services/nnid/routes/provider.ts @@ -0,0 +1,192 @@ +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import { getServerByTitleId, getServerByGameServerId } from '@/database'; +import { generateToken, getValueFromHeaders, getValueFromQueryString } from '@/util'; +import { NEXAccount } from '@/models/nex-account'; +import { TokenOptions } from '@/types/common/token-options'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; +import { HydratedServerDocument } from '@/types/mongoose/server'; +import { HydratedNEXAccountDocument } from '@/types/mongoose/nex-account'; + +const router: express.Router = express.Router(); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/provider/service_token/@me + * Description: Gets a service token + */ +router.get('/service_token/@me', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + response.status(400); + + return response.end(xmlbuilder.create({ + errors: { + error: { + cause: 'access_token', + code: '0002', + message: 'Invalid access token' + } + } + }).end()); + } + + const titleID: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-title-id'); + + if (!titleID) { + // TODO - Research this error more + return response.send(xmlbuilder.create({ + errors: { + error: { + code: '1021', + message: 'The requested game server was not found' + } + } + }).end()); + } + + const serverAccessLevel: string = pnid.server_access_level; + const server: HydratedServerDocument | null = await getServerByTitleId(titleID, serverAccessLevel); + + if (!server || !server.aes_key) { + return response.send(xmlbuilder.create({ + errors: { + error: { + code: '1021', + message: 'The requested game server was not found' + } + } + }).end()); + } + + const tokenOptions: TokenOptions = { + system_type: server.device, + token_type: 0x4, // * Service token + pid: pnid.pid, + access_level: pnid.access_level, + title_id: BigInt(parseInt(titleID, 16)), + expire_time: BigInt(Date.now() + (3600 * 1000)) + }; + + const serviceTokenBuffer: Buffer | null = await generateToken(server.aes_key, tokenOptions); + let serviceToken: string = serviceTokenBuffer ? serviceTokenBuffer.toString('base64') : ''; + + if (request.isCemu) { + serviceToken = Buffer.from(serviceToken, 'base64').toString('hex'); + } + + response.send(xmlbuilder.create({ + service_token: { + token: serviceToken + } + }).end()); +}); + +/** + * [GET] + * Replacement for: https://account.nintendo.net/v1/api/provider/nex_token/@me + * Description: Gets a NEX server address and token + */ +router.get('/nex_token/@me', async (request: express.Request, response: express.Response) => { + const pnid: HydratedPNIDDocument | null = request.pnid; + + if (!pnid) { + response.status(400); + + return response.end(xmlbuilder.create({ + errors: { + error: { + cause: 'access_token', + code: '0002', + message: 'Invalid access token' + } + } + }).end()); + } + + const nexAccount: HydratedNEXAccountDocument | null = await NEXAccount.findOne({ + owning_pid: pnid.pid + }); + + if (!nexAccount) { + return response.status(404).send(xmlbuilder.create({ + errors: { + error: { + cause: '', + code: '0008', + message: 'Not Found' + } + } + }).end()); + } + + const gameServerID: string | undefined = getValueFromQueryString(request.query, 'game_server_id'); + + if (!gameServerID) { + return response.send(xmlbuilder.create({ + errors: { + error: { + code: '0118', + message: 'Unique ID and Game Server ID are not linked' + } + } + }).end()); + } + + const serverAccessLevel: string = pnid.server_access_level; + const server: HydratedServerDocument | null = await getServerByGameServerId(gameServerID, serverAccessLevel); + + if (!server || !server.aes_key) { + return response.send(xmlbuilder.create({ + errors: { + error: { + code: '1021', + message: 'The requested game server was not found' + } + } + }).end()); + } + + const titleID: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-title-id'); + + if (!titleID) { + // TODO - Research this error more + return response.send(xmlbuilder.create({ + errors: { + error: { + code: '1021', + message: 'The requested game server was not found' + } + } + }).end()); + } + + const tokenOptions: TokenOptions = { + system_type: server.device, + token_type: 0x3, // nex token, + pid: pnid.pid, + access_level: pnid.access_level, + title_id: BigInt(parseInt(titleID, 16)), + expire_time: BigInt(Date.now() + (3600 * 1000)) + }; + + const nexTokenBuffer: Buffer | null = await generateToken(server.aes_key, tokenOptions); + let nexToken: string = nexTokenBuffer ? nexTokenBuffer.toString('base64') : ''; + + if (request.isCemu) { + nexToken = Buffer.from(nexToken || '', 'base64').toString('hex'); + } + + response.send(xmlbuilder.create({ + nex_token: { + host: server.ip, + nex_password: nexAccount.password, + pid: nexAccount.pid, + port: server.port, + token: nexToken + } + }).end()); +}); + +export default router; \ No newline at end of file diff --git a/src/services/nnid/routes/support.js b/src/services/nnid/routes/support.ts similarity index 53% rename from src/services/nnid/routes/support.js rename to src/services/nnid/routes/support.ts index d63fea3..be795d6 100644 --- a/src/services/nnid/routes/support.js +++ b/src/services/nnid/routes/support.ts @@ -1,17 +1,20 @@ -const router = require('express').Router(); -const dns = require('dns'); -const xmlbuilder = require('xmlbuilder'); -const moment = require('moment'); -const util = require('../../../util'); -const database = require('../../../database'); +import dns from 'node:dns'; +import express from 'express'; +import xmlbuilder from 'xmlbuilder'; +import moment from 'moment'; +import { getPNIDByPID } from '@/database'; +import { sendEmailConfirmedEmail, sendConfirmationEmail, sendForgotPasswordEmail } from '@/util'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; + +const router: express.Router = express.Router(); /** * [POST] * Replacement for: https://account.nintendo.net/v1/api/support/validate/email * Description: Verifies a provided email address is valid */ -router.post('/validate/email', async (request, response) => { - const { email } = request.body; +router.post('/validate/email', async (request: express.Request, response: express.Response) => { + const email: string = request.body.email; if (!email) { return response.send(xmlbuilder.create({ @@ -25,9 +28,9 @@ router.post('/validate/email', async (request, response) => { }).end()); } - const domain = email.split('@')[1]; + const domain: string = email.split('@')[1]; - dns.resolveMx(domain, (error) => { + dns.resolveMx(domain, (error: NodeJS.ErrnoException | null) => { if (error) { return response.send(xmlbuilder.create({ errors: { @@ -49,10 +52,11 @@ router.post('/validate/email', async (request, response) => { * Replacement for: https://account.nintendo.net/v1/api/support/email_confirmation/:pid/:code * Description: Verifies a users email via 6 digit code */ -router.put('/email_confirmation/:pid/:code', async (request, response) => { - const { pid, code } = request.params; +router.put('/email_confirmation/:pid/:code', async (request: express.Request, response: express.Response) => { + const code: string = request.params.code; + const pid: number = Number(request.params.pid); - const pnid = await database.getUserByPID(pid); + const pnid: HydratedPNIDDocument | null = await getPNIDByPID(pid); if (!pnid) { return response.status(400).send(xmlbuilder.create({ @@ -65,7 +69,7 @@ router.put('/email_confirmation/:pid/:code', async (request, response) => { }).end()); } - if (pnid.get('identification.email_code') !== code) { + if (pnid.identification.email_code !== code) { return response.status(400).send(xmlbuilder.create({ errors: { error: { @@ -76,15 +80,15 @@ router.put('/email_confirmation/:pid/:code', async (request, response) => { }).end()); } - const validatedDate = moment().format('YYYY-MM-DDTHH:MM:SS'); + const validatedDate: string = moment().format('YYYY-MM-DDTHH:MM:SS'); - pnid.set('email.reachable', true); - pnid.set('email.validated', true); - pnid.set('email.validated_date', validatedDate); + pnid.email.reachable = true; + pnid.email.validated = true; + pnid.email.validated_date = validatedDate; await pnid.save(); - await util.sendEmailConfirmedEmail(pnid); + await sendEmailConfirmedEmail(pnid); response.status(200).send(''); }); @@ -94,10 +98,10 @@ router.put('/email_confirmation/:pid/:code', async (request, response) => { * Replacement for: https://account.nintendo.net/v1/api/support/resend_confirmation * Description: Resends a users confirmation email */ -router.get('/resend_confirmation', async (request, response) => { - const pid = request.headers['x-nintendo-pid']; +router.get('/resend_confirmation', async (request: express.Request, response: express.Response) => { + const pid: number = Number(request.headers['x-nintendo-pid']); - const pnid = await database.getUserByPID(pid); + const pnid: HydratedPNIDDocument | null = await getPNIDByPID(pid); if (!pnid) { // TODO - Unsure if this is the right error @@ -111,7 +115,7 @@ router.get('/resend_confirmation', async (request, response) => { }).end()); } - await util.sendConfirmationEmail(pnid); + await sendConfirmationEmail(pnid); response.status(200).send(''); }); @@ -122,10 +126,10 @@ router.get('/resend_confirmation', async (request, response) => { * Description: Sends the user a password reset email * NOTE: On NN this was a temp password that expired after 24 hours. We do not do that */ -router.get('/forgotten_password/:pid', async (request, response) => { - const { pid } = request.params; +router.get('/forgotten_password/:pid', async (request: express.Request, response: express.Response) => { + const pid: number = Number(request.params.pid); - const pnid = await database.getUserByPID(pid); + const pnid: HydratedPNIDDocument | null = await getPNIDByPID(pid); if (!pnid) { // TODO - Better errors @@ -140,9 +144,9 @@ router.get('/forgotten_password/:pid', async (request, response) => { }).end()); } - await util.sendForgotPasswordEmail(pnid); + await sendForgotPasswordEmail(pnid); response.status(200).send(''); }); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/src/types/common/config.ts b/src/types/common/config.ts new file mode 100644 index 0000000..4780d00 --- /dev/null +++ b/src/types/common/config.ts @@ -0,0 +1,48 @@ +import mongoose from 'mongoose'; + +export interface Config { + http: { + port: number; + }; + mongoose: { + connection_string: string; + options: mongoose.ConnectOptions; + }; + redis: { + client: { + url: string; + } + }; + email: { + host: string; + port: number; + secure: boolean; + auth: { + user: string; + pass: string; + }; + from: string; + }; + s3: { + endpoint: string; + key: string; + secret: string; + }; + hcaptcha: { + secret: string; + }; + cdn: { + subdomain: string; + disk_path: string; + base_url: string; + }; + website_base: string; + aes_key: string; +} + +export interface DisabledFeatures { + redis: boolean; + email: boolean; + captcha: boolean; + s3: boolean +} \ No newline at end of file diff --git a/src/types/common/mailer-options.ts b/src/types/common/mailer-options.ts new file mode 100644 index 0000000..265b42a --- /dev/null +++ b/src/types/common/mailer-options.ts @@ -0,0 +1,16 @@ +export interface MailerOptions { + to: string; + subject: string; + username: string; + paragraph?: string; + preview?: string; + text: string; + link?: { + href: string; + text: string; + }; + confirmation?: { + href: string; + code: string; + }; +} \ No newline at end of file diff --git a/src/types/common/safe-qs.ts b/src/types/common/safe-qs.ts new file mode 100644 index 0000000..a17df29 --- /dev/null +++ b/src/types/common/safe-qs.ts @@ -0,0 +1,3 @@ +export interface SafeQs { + [key: string]: string | undefined +} \ No newline at end of file diff --git a/src/types/common/signature-size.ts b/src/types/common/signature-size.ts new file mode 100644 index 0000000..c9f9bbf --- /dev/null +++ b/src/types/common/signature-size.ts @@ -0,0 +1,4 @@ +export interface SignatureSize { + SIZE: number; + PADDING_SIZE: number; +} \ No newline at end of file diff --git a/src/types/common/token-options.ts b/src/types/common/token-options.ts new file mode 100644 index 0000000..6dcc85e --- /dev/null +++ b/src/types/common/token-options.ts @@ -0,0 +1,8 @@ +export interface TokenOptions { + system_type: number; + token_type: number; + pid: number; + access_level?: number; + title_id?: bigint; + expire_time: bigint; +} \ No newline at end of file diff --git a/src/types/common/token.ts b/src/types/common/token.ts new file mode 100644 index 0000000..4c7c558 --- /dev/null +++ b/src/types/common/token.ts @@ -0,0 +1,8 @@ +export interface Token { + system_type: number; + token_type: number; + pid: number; + access_level?: number; + title_id?: bigint; + expire_time: bigint; +} \ No newline at end of file diff --git a/src/types/common/yes-no-bool-string.ts b/src/types/common/yes-no-bool-string.ts new file mode 100644 index 0000000..474aa6e --- /dev/null +++ b/src/types/common/yes-no-bool-string.ts @@ -0,0 +1 @@ +export type YesNoBoolString = 'Y' | 'N'; \ No newline at end of file diff --git a/src/types/express-subdomain.d.ts b/src/types/express-subdomain.d.ts new file mode 100644 index 0000000..933d1f0 --- /dev/null +++ b/src/types/express-subdomain.d.ts @@ -0,0 +1,16 @@ +// * Credit to https://github.com/bmullan91/express-subdomain/pull/61 for the types! + +declare module 'express-subdomain'{ + import type { Request, Response, Router } from 'express'; + + /** + * @description The subdomain function. + * @param subdomain The subdomain to listen on. + * @param fn The listener function, takes a response and request. + * @returns A function call to the value passed as FN, or void (the next function). + */ + export default function subdomain( + subdomain: string, + fn: Router | ((req: Request, res: Response) => void | any) + ): (req: Request, res: Response, next: () => void) => void | typeof fn; +} \ No newline at end of file diff --git a/src/types/express.d.ts b/src/types/express.d.ts new file mode 100644 index 0000000..4a14525 --- /dev/null +++ b/src/types/express.d.ts @@ -0,0 +1,15 @@ +import NintendoCertificate from '@/nintendo-certificate'; +import { HydratedPNIDDocument } from '@/types/mongoose/pnid'; +import { HydratedNEXAccountDocument } from '@/types/mongoose/nex-account'; + +declare global { + namespace Express { + interface Request { + pnid: HydratedPNIDDocument | null; + nexAccount: HydratedNEXAccountDocument | null; + isCemu?: boolean; + files?: Record; + certificate?: NintendoCertificate; + } + } +} \ No newline at end of file diff --git a/src/types/image-pixels.d.ts b/src/types/image-pixels.d.ts new file mode 100644 index 0000000..762209c --- /dev/null +++ b/src/types/image-pixels.d.ts @@ -0,0 +1,34 @@ +declare module 'image-pixels' { + import { NdArray } from 'ndarray'; + + export interface ImagePixelsOptions { + source: ImageSource; + shape: [number, number]; + width: number; + height: number; + type: string; + mime: string; + clip: [number, number, number, number] | { + x?: number; + y?: number; + width?: number; + height?: number; + }; + cache: boolean; + } + + export type ImageSource = string | + HTMLImageElement | SVGImageElement | HTMLVideoElement | CSSImageValue | + typeof Image | ImageData | ImageBitmap | + File | Blob | + //Canvas | Context2D | + //WebGLContext | + Buffer | ArrayBuffer | Uint8Array | Uint8ClampedArray | + Float32Array | Float64Array | Array | Array> | + NdArray | + ImagePixelsOptions; + + export default function pixels( + src: ImageSource | Promise + ): ImageData; +} \ No newline at end of file diff --git a/src/types/mii-js.d.ts b/src/types/mii-js.d.ts new file mode 100644 index 0000000..a63a5b8 --- /dev/null +++ b/src/types/mii-js.d.ts @@ -0,0 +1,99 @@ +declare module 'mii-js'; + +// TODO - These have limits, and should be validated + +type MiiStudioURLOptions = { + type?: string; + expression?: string; + width?: number; + bgColor?: string; + clothesColor?: string; + cameraXRotate?: number; + cameraYRotate?: number; + cameraZRotate?: number; + characterXRotate?: number; + characterYRotate?: number; + characterZRotate?: number; + lightXDirection?: number; + lightYDirection?: number; + lightZDirection?: number; + lightDirectionMode?: string; + instanceCount?: number; + instanceRotationMode?: string; +}; + +type Mii = { + version: number; + allowCopying: boolean; + profanityFlag: boolean; + regionLock: number; + characterSet: number; + pageIndex: number; + slotIndex: number; + unknown1: number; + deviceOrigin: number; + systemId: Buffer; + normalMii: boolean; + dsMii: boolean; + nonUserMii: boolean; + isValid: boolean; + creationTime: number; + consoleMAC: Buffer; + gender: number; + birthMonth: number; + birthDay: number; + favoriteColor: number; + favorite: boolean; + miiName: string; + height: number; + build: number; + disableSharing: boolean; + faceType: number; + skinColor: number; + wrinklesType: number; + makeupType: number; + hairType: number; + hairColor: number; + flipHair: boolean; + eyeType: number; + eyeColor: number; + eyeScale: number; + eyeVerticalStretch: number; + eyeRotation: number; + eyeSpacing: number; + eyeYPosition: number; + eyebrowType: number; + eyebrowColor: number; + eyebrowScale: number; + eyebrowVerticalStretch: number; + eyebrowRotation: number; + eyebrowSpacing: number; + eyebrowYPosition: number; + noseType: number; + noseScale: number; + noseYPosition: number; + mouthType: number; + mouthColor: number; + mouthScale: number; + mouthHorizontalStretch: number; + mouthYPosition: number; + mustacheType: number; + unknown2: number; + beardType: number; + facialHairColor: number; + mustacheScale: number; + mustacheYPosition: number; + glassesType: number; + glassesColor: number; + glassesScale: number; + glassesYPosition: number; + moleEnabled: boolean; + moleScale: number; + moleXPosition: number; + moleYPosition: number; + creatorName: string; + checksum: number; + + studioUrl: (options: MiiStudioURLOptions) => string; + encode: () => Buffer; +}; \ No newline at end of file diff --git a/src/types/mongoose/device-attribute.ts b/src/types/mongoose/device-attribute.ts new file mode 100644 index 0000000..e9eb87f --- /dev/null +++ b/src/types/mongoose/device-attribute.ts @@ -0,0 +1,13 @@ +import { Model } from 'mongoose'; + +export interface IDeviceAttribute { + created_date?: string; + name: string; + value: string; +} + +export interface IDeviceAttributeMethods {} + +interface IDeviceAttributeQueryHelpers {} + +export interface DeviceAttributeModel extends Model {} \ No newline at end of file diff --git a/src/types/mongoose/device.ts b/src/types/mongoose/device.ts new file mode 100644 index 0000000..a2b2c78 --- /dev/null +++ b/src/types/mongoose/device.ts @@ -0,0 +1,34 @@ +import { Model, Types, HydratedDocument } from 'mongoose'; +import { IDeviceAttribute } from '@/types/mongoose/device-attribute'; + +type MODEL = 'wup' | 'ctr' | 'spr' | 'ftr' | 'ktr' | 'red' | 'jan'; +type ACCESS_LEVEL = 0 | 1 | 2 | 3; +type SERVER_ACCESS_LEVEL = 'prod' | 'test' | 'dev'; + +export interface IDevice { + is_emulator: boolean; + model: MODEL; + device_id: number; + device_type: number; + serial: string; + device_attributes: Types.DocumentArray; + soap: { + token: string; + account_id: number; + }; + // * 3DS-specific stuff + environment: string; + mac_hash: string; + fcdcert_hash: string; + linked_pids: number[]; + access_level: ACCESS_LEVEL; + server_access_level: SERVER_ACCESS_LEVEL; +} + +export interface IDeviceMethods {} + +interface IDeviceQueryHelpers {} + +export interface DeviceModel extends Model {} + +export type HydratedDeviceDocument = HydratedDocument \ No newline at end of file diff --git a/src/types/mongoose/nex-account.ts b/src/types/mongoose/nex-account.ts new file mode 100644 index 0000000..99342e4 --- /dev/null +++ b/src/types/mongoose/nex-account.ts @@ -0,0 +1,29 @@ +import { Model, HydratedDocument } from 'mongoose'; + +type DEVICE = 'wiiu' | '3ds'; +enum ACCESS_LEVEL { + User = 0, + Admin = 1, + Owner = 2, + Dev = 3 +} + +export interface INEXAccount { + device_type: DEVICE; + pid: number; + password: string; + owning_pid: number; + access_level: ACCESS_LEVEL; + server_access_level: string; +} + +export interface INEXAccountMethods { + generatePID(): Promise; + generatePassword(): void; +} + +interface INEXAccountQueryHelpers {} + +export interface NEXAccountModel extends Model {} + +export type HydratedNEXAccountDocument = HydratedDocument \ No newline at end of file diff --git a/src/types/mongoose/pnid.ts b/src/types/mongoose/pnid.ts new file mode 100644 index 0000000..ee1fda6 --- /dev/null +++ b/src/types/mongoose/pnid.ts @@ -0,0 +1,80 @@ +import { Model, Types, HydratedDocument } from 'mongoose'; +import { IDevice } from '@/types/mongoose/device'; + +export interface IPNID { + access_level: number; + server_access_level: string; + pid: number; + creation_date: string; + updated: string; + username: string; + usernameLower: string; + password: string; + birthdate: string; + gender: string; + country: string; + language: string; + email: { + address: string; + primary: boolean; + parent: boolean; + reachable: boolean; + validated: boolean; + validated_date: string; + id: number; + }; + region: number; + timezone: { + name: string; + offset: number; + marketing: boolean; + off_device: boolean; + }; + mii: { + name: string; + primary: boolean; + data: string; + id: number; + hash: string; + image_url: string; + image_id: number; + }; + flags: { + active: boolean; + marketing: boolean; + off_device: boolean; + }; + devices: Types.DocumentArray; + identification: { // user identification tokens + email_code: string; + email_token: string; + access_token: { + value: string; + ttl: number; + }; + refresh_token: { + value: string; + ttl: number; + } + }; + connections: { + discord: { + id: string; + } + }; +} + +export interface IPNIDMethods { + generatePID(): Promise; + generateEmailValidationCode(): Promise; + generateEmailValidationToken(): Promise; + updateMii(mii: { name: string, primary: string, data: string}): Promise; + generateMiiImages(): Promise; + getServerMode(): string; +} + +interface IPNIDQueryHelpers {} + +export interface PNIDModel extends Model {} + +export type HydratedPNIDDocument = HydratedDocument \ No newline at end of file diff --git a/src/types/mongoose/server.ts b/src/types/mongoose/server.ts new file mode 100644 index 0000000..02fc372 --- /dev/null +++ b/src/types/mongoose/server.ts @@ -0,0 +1,22 @@ +import { Model, HydratedDocument } from 'mongoose'; + +export interface IServer { + ip: string; + port: number; + service_name: string; + service_type: string; + game_server_id: string; + title_ids: string[]; + access_mode: string; + maintenance_mode: boolean; + device: number; + aes_key: string; +} + +export interface IServerMethods {} + +interface IServerQueryHelpers {} + +export interface ServerModel extends Model {} + +export type HydratedServerDocument = HydratedDocument \ No newline at end of file diff --git a/src/types/services/api/connection-data.ts b/src/types/services/api/connection-data.ts new file mode 100644 index 0000000..54fe992 --- /dev/null +++ b/src/types/services/api/connection-data.ts @@ -0,0 +1,4 @@ +import { DiscordConnectionData } from '@/types/services/api/discord-connection-data'; + +// TODO - This will be a union of all ConnectionData types when more connections are added +export type ConnectionData = DiscordConnectionData; \ No newline at end of file diff --git a/src/types/services/api/connection-response.ts b/src/types/services/api/connection-response.ts new file mode 100644 index 0000000..6baa9b0 --- /dev/null +++ b/src/types/services/api/connection-response.ts @@ -0,0 +1,5 @@ +export interface ConnectionResponse { + app: string; + status: number; + error?: string; +} \ No newline at end of file diff --git a/src/types/services/api/discord-connection-data.ts b/src/types/services/api/discord-connection-data.ts new file mode 100644 index 0000000..0ee459a --- /dev/null +++ b/src/types/services/api/discord-connection-data.ts @@ -0,0 +1,3 @@ +export interface DiscordConnectionData { + id: string; +} \ No newline at end of file diff --git a/src/types/services/api/update-user-request.ts b/src/types/services/api/update-user-request.ts new file mode 100644 index 0000000..a525e98 --- /dev/null +++ b/src/types/services/api/update-user-request.ts @@ -0,0 +1,7 @@ +export interface UpdateUserRequest { + mii?: { + name: string; + primary: string; + data: string; + } +} \ No newline at end of file diff --git a/src/types/services/nasc/request-params.ts b/src/types/services/nasc/request-params.ts new file mode 100644 index 0000000..aafcda6 --- /dev/null +++ b/src/types/services/nasc/request-params.ts @@ -0,0 +1,11 @@ +export interface NASCRequestParams { + action: string; + fcdcert: string; + csnum: string; + macadr: string; + titleid: string; + servertype: string; + userid?: string; + uidhmac?: string; + passwd?: string; +} \ No newline at end of file diff --git a/src/types/services/nnid/person.ts b/src/types/services/nnid/person.ts new file mode 100644 index 0000000..00958e5 --- /dev/null +++ b/src/types/services/nnid/person.ts @@ -0,0 +1,44 @@ +import { YesNoBoolString } from '@/types/common/yes-no-bool-string'; +import { IDeviceAttribute } from '@/types/mongoose/device-attribute'; + +// TODO - Change this Map type to something more strongly typed +export interface Person { + birth_date: string; + user_id: string; + password: string; + country: string; + language: string; + tz_name: string; + agreement: { + agreement_date: string; + country: string; + location: string; + type: string; + version: string; + }; + email: { + address: string; + owned: YesNoBoolString; + parent: YesNoBoolString; + primary: YesNoBoolString; + validated: YesNoBoolString; + type: string; + }; + mii: { + name: string; + primary: YesNoBoolString; + data: string; + }; + parental_consent: { + scope: string; + consent_date: string; + approval_id: string; + }; + gender: string; + region: number; + marketing_flag: YesNoBoolString; + device_attributes: { + device_attribute: IDeviceAttribute[] + }; + off_device_flag: YesNoBoolString; +} \ No newline at end of file diff --git a/src/types/services/nnid/pnid-profile.ts b/src/types/services/nnid/pnid-profile.ts new file mode 100644 index 0000000..cb02669 --- /dev/null +++ b/src/types/services/nnid/pnid-profile.ts @@ -0,0 +1,53 @@ +import { YesNoBoolString } from '@/types/common/yes-no-bool-string'; + +export interface PNIDProfile { + //accounts: {}; // * We need to figure this out; no idea what these values mean or what they do + active_flag: YesNoBoolString; + birth_date: string; + country: string; + create_date: string; + device_attributes: { + device_attribute: { + name: string; + value: string; + created_date: string; + }; + }[]; + gender: string; + language: string; + updated: string; + marketing_flag: YesNoBoolString; + off_device_flag: YesNoBoolString; + pid: number; + email: { + address: string; + id: number; + parent: YesNoBoolString; + primary: YesNoBoolString; + reachable: YesNoBoolString; + type: 'DEFAULT'; + updated_by: 'USER'; // * Can also be INTERNAL WS; don't know the difference + validated: YesNoBoolString; + validated_date: string; + }; + mii: { + status: 'COMPLETED'; + data: string; + id: number; + mii_hash: string; + mii_images: { + mii_image: { + cached_url: string; + id: number; + url: string; + type: 'standard'; + } + }; + name: string; + primary: YesNoBoolString; + }; + region: number; + tz_name: string; + user_id: string; + utc_offset: number; +} \ No newline at end of file diff --git a/src/types/services/nnid/region-languages.ts b/src/types/services/nnid/region-languages.ts new file mode 100644 index 0000000..e25307d --- /dev/null +++ b/src/types/services/nnid/region-languages.ts @@ -0,0 +1,5 @@ +import { RegionTimezones } from '@/types/services/nnid/region-timezones'; + +export interface RegionLanguages { + [myKey: string]: RegionTimezones +} \ No newline at end of file diff --git a/src/types/services/nnid/region-timezones.ts b/src/types/services/nnid/region-timezones.ts new file mode 100644 index 0000000..c904aaa --- /dev/null +++ b/src/types/services/nnid/region-timezones.ts @@ -0,0 +1,9 @@ +export interface RegionTimezone { + area: string; + language: string; + name: string; + utc_offset: string; + order: string; +} + +export type RegionTimezones = RegionTimezone[]; \ No newline at end of file diff --git a/src/types/tga.d.ts b/src/types/tga.d.ts new file mode 100644 index 0000000..16eb4a2 --- /dev/null +++ b/src/types/tga.d.ts @@ -0,0 +1,58 @@ +declare module 'tga' { + export interface TGAHeader { + idLength: number; + colorMapType: number; + dataType: number; + colorMapOrigin: number; + colorMapLength: number; + colorMapDepth: number; + xOrigin: number; + yOrigin: number; + width: number; + height: number; + bitsPerPixel: number; + flags: number; + id: string; + } + + export default class TGA { + constructor(buf: Buffer, opt?: { dontFixAlpha: boolean }); + + static createTgaBuffer(width: number, height: number, pixels: Uint8Array, dontFlipY: boolean): Buffer; + + parseHeader(): void; + parseFooter(): void; + parseExtension(extensionAreaOffset: number): void; + readColor(offset: number, bytesPerPixel: number): Uint8Array; + readColorWithColorMap(offset: number): Uint8Array; + readColorAuto(offset: number, bytesPerPixel: number, isUsingColorMap: boolean): Uint8Array; + parseColorMap(): void; + setPixel(pixels: Uint8Array, idx: number, color: Uint8Array): void; + parsePixels(): void; + parse(): void; + fixForAlpha(): void; + + public dontFixAlpha: boolean; + public _buff: Buffer; + public data: Uint8Array; + public currentOffset: number; + + public header: TGAHeader; + + public width: number; + public height: number; + + public isUsingColorMap: boolean; + public isUsingRLE: boolean; + public isGray: boolean; + + public hasAlpha: boolean; + + public isFlipX: boolean; + public isFlipY: boolean; + + public colorMap: Uint8Array; + + public pixels: Uint8Array; + } +} \ No newline at end of file diff --git a/src/util.js b/src/util.js deleted file mode 100644 index 0050b38..0000000 --- a/src/util.js +++ /dev/null @@ -1,319 +0,0 @@ -const crypto = require('crypto'); -const path = require('path'); -const NodeRSA = require('node-rsa'); -const aws = require('aws-sdk'); -const fs = require('fs-extra'); -const mailer = require('./mailer'); -const cache = require('./cache'); -const { config, disabledFeatures } = require('./config-manager'); - -let s3; - -if (!disabledFeatures.s3) { - s3 = new aws.S3({ - endpoint: new aws.Endpoint(config.s3.endpoint), - accessKeyId: config.s3.key, - secretAccessKey: config.s3.secret - }); -} - -function nintendoPasswordHash(password, pid) { - const pidBuffer = Buffer.alloc(4); - pidBuffer.writeUInt32LE(pid); - - const unpacked = Buffer.concat([ - pidBuffer, - Buffer.from('\x02\x65\x43\x46'), - Buffer.from(password) - ]); - const hashed = crypto.createHash('sha256').update(unpacked).digest().toString('hex'); - - return hashed; -} - -function nintendoBase64Decode(encoded) { - encoded = encoded.replaceAll('.', '+').replaceAll('-', '/').replaceAll('*', '='); - return Buffer.from(encoded, 'base64'); -} - -function nintendoBase64Encode(decoded) { - const encoded = Buffer.from(decoded).toString('base64'); - return encoded.replaceAll('+', '.').replaceAll('/', '-').replaceAll('=', '*'); -} - -async function generateToken(cryptoOptions, tokenOptions) { - - // Access and refresh tokens use a different format since they must be much smaller - // They take no extra crypto options - if (!cryptoOptions) { - const aesKey = await cache.getServiceAESKey('account', 'hex'); - - const dataBuffer = Buffer.alloc(1 + 1 + 4 + 8); - - dataBuffer.writeUInt8(tokenOptions.system_type, 0x0); - dataBuffer.writeUInt8(tokenOptions.token_type, 0x1); - dataBuffer.writeUInt32LE(tokenOptions.pid, 0x2); - dataBuffer.writeBigUInt64LE(tokenOptions.expire_time, 0x6); - - const iv = Buffer.alloc(16); - const cipher = crypto.createCipheriv('aes-128-cbc', aesKey, iv); - - let encryptedBody = cipher.update(dataBuffer); - encryptedBody = Buffer.concat([encryptedBody, cipher.final()]); - - return encryptedBody.toString('base64'); - } - - const publicKey = new NodeRSA(cryptoOptions.public_key, 'pkcs8-public-pem', { - environment: 'browser', - encryptionScheme: { - 'hash': 'sha256', - } - }); - - // Create the buffer containing the token data - const dataBuffer = Buffer.alloc(1 + 1 + 4 + 1 + 8 + 8); - - dataBuffer.writeUInt8(tokenOptions.system_type, 0x0); - dataBuffer.writeUInt8(tokenOptions.token_type, 0x1); - dataBuffer.writeUInt32LE(tokenOptions.pid, 0x2); - dataBuffer.writeUInt8(tokenOptions.access_level, 0x6); - dataBuffer.writeBigUInt64LE(tokenOptions.title_id, 0x7); - dataBuffer.writeBigUInt64LE(tokenOptions.expire_time, 0xF); - - // Calculate the signature of the token body - const hmac = crypto.createHmac('sha1', cryptoOptions.hmac_secret).update(dataBuffer); - const signature = hmac.digest(); - - // You can thank the 3DS for the shit thats about to happen with the AES IV - // The 3DS only allows for strings up to 255 characters in NEX - // So this is done to reduce the token size as much as possible - // I am sorry, and have already asked every God I could think of for forgiveness - - // Generate random AES key - const key = crypto.randomBytes(16); - - // Encrypt the AES key with RSA public key - const encryptedKey = publicKey.encrypt(key); - - // Take two random points in the RSA encrypted key - const point1 = ~~((encryptedKey.length - 8) * Math.random()); - const point2 = ~~((encryptedKey.length - 8) * Math.random()); - - // Build an IV from each of the two points - const iv = Buffer.concat([ - Buffer.from(encryptedKey.subarray(point1, point1 + 8)), - Buffer.from(encryptedKey.subarray(point2, point2 + 8)) - ]); - - const cipher = crypto.createCipheriv('aes-128-cbc', key, iv); - - // Encrypt the token body with AES - let encryptedBody = cipher.update(dataBuffer); - encryptedBody = Buffer.concat([encryptedBody, cipher.final()]); - - // Create crypto config token section - const cryptoConfig = Buffer.concat([ - encryptedKey, - Buffer.from([point1, point2]) - ]); - - // Build the token - const token = Buffer.concat([ - cryptoConfig, - signature, - encryptedBody - ]); - - return token.toString('base64'); // Encode to base64 for transport -} - -async function decryptToken(token) { - // Access and refresh tokens use a different format since they must be much smaller - // Assume a small length means access or refresh token - if (token.length <= 32) { - const aesKey = await cache.getServiceAESKey('account', 'hex'); - - const iv = Buffer.alloc(16); - - const decipher = crypto.createDecipheriv('aes-128-cbc', aesKey, iv); - - let decryptedBody = decipher.update(token); - decryptedBody = Buffer.concat([decryptedBody, decipher.final()]); - - return decryptedBody; - } - - const privateKeyBytes = await cache.getServicePrivateKey('account'); - const secretKey = await cache.getServiceSecretKey('account'); - - const privateKey = new NodeRSA(privateKeyBytes, 'pkcs1-private-pem', { - environment: 'browser', - encryptionScheme: { - 'hash': 'sha256', - } - }); - - const cryptoConfig = token.subarray(0, 0x82); - const signature = token.subarray(0x82, 0x96); - const encryptedBody = token.subarray(0x96); - - const encryptedAESKey = cryptoConfig.subarray(0, 128); - const point1 = cryptoConfig.readInt8(0x80); - const point2 = cryptoConfig.readInt8(0x81); - - const iv = Buffer.concat([ - Buffer.from(encryptedAESKey.subarray(point1, point1 + 8)), - Buffer.from(encryptedAESKey.subarray(point2, point2 + 8)) - ]); - - const decryptedAESKey = privateKey.decrypt(encryptedAESKey); - - const decipher = crypto.createDecipheriv('aes-128-cbc', decryptedAESKey, iv); - - let decryptedBody = decipher.update(encryptedBody); - decryptedBody = Buffer.concat([decryptedBody, decipher.final()]); - - const hmac = crypto.createHmac('sha1', secretKey).update(decryptedBody); - const calculatedSignature = hmac.digest(); - - if (!signature.equals(calculatedSignature)) { - console.log('Token signature did not match'); - return null; - } - - return decryptedBody; -} - -function unpackToken(token) { - if (token.length <= 14) { - return { - system_type: token.readUInt8(0x0), - token_type: token.readUInt8(0x1), - pid: token.readUInt32LE(0x2), - expire_time: token.readBigUInt64LE(0x6) - }; - } - - return { - system_type: token.readUInt8(0x0), - token_type: token.readUInt8(0x1), - pid: token.readUInt32LE(0x2), - access_level: token.readUInt8(0x6), - title_id: token.readBigUInt64LE(0x7), - expire_time: token.readBigUInt64LE(0xF) - }; -} - -function fullUrl(request) { - const protocol = request.protocol; - const host = request.host; - const opath = request.originalUrl; - - return `${protocol}://${host}${opath}`; -} - -async function uploadCDNAsset(bucket, key, data, acl) { - if (disabledFeatures.s3) { - await writeLocalCDNFile(key, data); - } else { - const awsPutParams = { - Body: data, - Key: key, - Bucket: bucket, - ACL: acl - }; - - await s3.putObject(awsPutParams).promise(); - } -} - -async function writeLocalCDNFile(key, data) { - const filePath = config.cdn.disk_path; - const folder = path.dirname(filePath); - - await fs.ensureDir(folder); - await fs.writeFile(filePath, data); -} - -function nascError(errorCode) { - const params = new URLSearchParams({ - retry: nintendoBase64Encode('1'), - returncd: errorCode == 'null' ? errorCode : nintendoBase64Encode(errorCode), - datetime: nintendoBase64Encode(Date.now().toString()), - }); - - return params; -} - -async function sendConfirmationEmail(pnid) { - await mailer.sendMail({ - to: pnid.get('email.address'), - subject: '[Pretendo Network] Please confirm your email address', - username: pnid.get('username'), - confirmation: { - href: `https://api.pretendo.cc/v1/email/verify?token=${pnid.get('identification.email_token')}`, - code: pnid.get('identification.email_code') - }, - text: `Hello ${pnid.get('username')}! \r\n\r\nYour Pretendo Network ID activation is almost complete. Please click the link to confirm your e-mail address and complete the activation process: \r\nhttps://api.pretendo.cc/v1/email/verify?token=${pnid.get('identification.email_token')} \r\n\r\nYou may also enter the following 6-digit code on your console: ${pnid.get('identification.email_code')}` - }); - -} - -async function sendEmailConfirmedEmail(pnid) { - await mailer.sendMail({ - to: pnid.get('email.address'), - subject: '[Pretendo Network] Email address confirmed', - username: pnid.get('username'), - paragraph: 'your email address has been confirmed. We hope you have fun on Pretendo Network!', - text: `Dear ${pnid.get('username')}, \r\n\r\nYour email address has been confirmed. We hope you have fun on Pretendo Network!` - }); -} - -async function sendForgotPasswordEmail(pnid) { - const publicKey = await cache.getServicePublicKey('account'); - const secretKey = await cache.getServiceSecretKey('account'); - - const cryptoOptions = { - public_key: publicKey, - hmac_secret: secretKey - }; - - const tokenOptions = { - system_type: 0xF, // API - token_type: 0x5, // Password reset - pid: pnid.get('pid'), - access_level: pnid.get('access_level'), - title_id: BigInt(0), - expire_time: BigInt(Date.now() + (24 * 60 * 60 * 1000)) // Only valid for 24 hours - }; - - const passwordResetToken = await generateToken(cryptoOptions, tokenOptions); - - await mailer.sendMail({ - to: pnid.get('email.address'), - subject: '[Pretendo Network] Forgot Password', - username: pnid.get('username'), - paragraph: 'a password reset has been requested from this account. If you did not request the password reset, please ignore this email. If you did request this password reset, please click the link below to reset your password.', - link: { - text: 'Reset password', - href: `${config.website_base}/account/reset-password?token=${encodeURIComponent(passwordResetToken)}` - }, - text: `Dear ${pnid.get('username')}, a password reset has been requested from this account. \r\n\r\nIf you did not request the password reset, please ignore this email. \r\nIf you did request this password reset, please click the link to reset your password: ${config.website_base}/account/reset-password?token=${encodeURIComponent(passwordResetToken)}` - }); -} - -module.exports = { - nintendoPasswordHash, - nintendoBase64Decode, - nintendoBase64Encode, - generateToken, - decryptToken, - unpackToken, - fullUrl, - uploadCDNAsset, - nascError, - sendConfirmationEmail, - sendEmailConfirmedEmail, - sendForgotPasswordEmail -}; \ No newline at end of file diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..a884cad --- /dev/null +++ b/src/util.ts @@ -0,0 +1,254 @@ +import crypto from 'node:crypto'; +import path from 'node:path'; +import aws from 'aws-sdk'; +import fs from 'fs-extra'; +import express from 'express'; +import mongoose from 'mongoose'; +import { ParsedQs } from 'qs'; +import { sendMail } from '@/mailer'; +import { config, disabledFeatures } from '@/config-manager'; +import { TokenOptions } from '@/types/common/token-options'; +import { Token } from '@/types/common/token'; +import { IPNID, IPNIDMethods } from '@/types/mongoose/pnid'; +import { MailerOptions } from '@/types/common/mailer-options'; +import { SafeQs } from '@/types/common/safe-qs'; +import { IncomingHttpHeaders } from 'node:http'; + +let s3: aws.S3; + +if (!disabledFeatures.s3) { + s3 = new aws.S3({ + endpoint: new aws.Endpoint(config.s3.endpoint), + accessKeyId: config.s3.key, + secretAccessKey: config.s3.secret + }); +} + +export function nintendoPasswordHash(password: string, pid: number): string { + const pidBuffer: Buffer = Buffer.alloc(4); + pidBuffer.writeUInt32LE(pid); + + const unpacked: Buffer = Buffer.concat([ + pidBuffer, + Buffer.from('\x02\x65\x43\x46'), + Buffer.from(password) + ]); + + return crypto.createHash('sha256').update(unpacked).digest().toString('hex'); +} + +export function nintendoBase64Decode(encoded: string): Buffer { + encoded = encoded.replaceAll('.', '+').replaceAll('-', '/').replaceAll('*', '='); + return Buffer.from(encoded, 'base64'); +} + +export function nintendoBase64Encode(decoded: string | Buffer): string { + const encoded: string = Buffer.from(decoded).toString('base64'); + return encoded.replaceAll('+', '.').replaceAll('/', '-').replaceAll('=', '*'); +} + +export function generateToken(key: string, options: TokenOptions): Buffer | null { + let dataBuffer: Buffer = Buffer.alloc(1 + 1 + 4 + 8); + + dataBuffer.writeUInt8(options.system_type, 0x0); + dataBuffer.writeUInt8(options.token_type, 0x1); + dataBuffer.writeUInt32LE(options.pid, 0x2); + dataBuffer.writeBigUInt64LE(options.expire_time, 0x6); + + if (options.token_type !== 0x1 && options.token_type !== 0x2) { + if (options.access_level === undefined || options.title_id === undefined) { + return null; + } + + dataBuffer = Buffer.concat([ + dataBuffer, + Buffer.alloc(8 + 1) + ]); + + dataBuffer.writeBigUInt64LE(options.title_id, 0xE); + dataBuffer.writeUInt8(options.access_level, 0x16); + } + + const iv: Buffer = Buffer.alloc(16); + const cipher: crypto.Cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key, 'hex'), iv); + + return Buffer.concat([ + cipher.update(dataBuffer), + cipher.final() + ]); +} + +export function decryptToken(key: string, token: Buffer): Buffer { + const iv: Buffer = Buffer.alloc(16); + const decipher: crypto.Decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key, 'hex'), iv); + + return Buffer.concat([ + decipher.update(token), + decipher.final() + ]); +} + +export function unpackToken(token: Buffer): Token { + const unpacked: Token = { + system_type: token.readUInt8(0x0), + token_type: token.readUInt8(0x1), + pid: token.readUInt32LE(0x2), + expire_time: token.readBigUInt64LE(0x6) + }; + + if (unpacked.token_type !== 0x1 && unpacked.token_type !== 0x2) { + unpacked.title_id = token.readBigUInt64LE(0xE); + unpacked.access_level = token.readUInt8(0x16); + } + + return unpacked; +} + +export function fullUrl(request: express.Request): string { + const protocol: string = request.protocol; + const host: string = request.host; + const opath: string = request.originalUrl; + + return `${protocol}://${host}${opath}`; +} + +export async function uploadCDNAsset(bucket: string, key: string, data: Buffer, acl: string): Promise { + if (disabledFeatures.s3) { + await writeLocalCDNFile(key, data); + } else { + await s3.putObject({ + Body: data, + Key: key, + Bucket: bucket, + ACL: acl + }).promise(); + } +} + +export async function writeLocalCDNFile(key: string, data: Buffer): Promise { + const filePath: string = config.cdn.disk_path; + const folder: string = path.dirname(filePath); + + await fs.ensureDir(folder); + await fs.writeFile(filePath, data); +} + +export function nascError(errorCode: string): URLSearchParams { + return new URLSearchParams({ + retry: nintendoBase64Encode('1'), + returncd: errorCode == 'null' ? errorCode : nintendoBase64Encode(errorCode), + datetime: nintendoBase64Encode(Date.now().toString()), + }); +} + +export async function sendConfirmationEmail(pnid: mongoose.HydratedDocument): Promise { + const options: MailerOptions = { + to: pnid.email.address, + subject: '[Pretendo Network] Please confirm your email address', + username: pnid.username, + confirmation: { + href: `https://api.pretendo.cc/v1/email/verify?token=${pnid.identification.email_token}`, + code: pnid.identification.email_code + }, + text: `Hello ${pnid.username}! \r\n\r\nYour Pretendo Network ID activation is almost complete. Please click the link to confirm your e-mail address and complete the activation process: \r\nhttps://api.pretendo.cc/v1/email/verify?token=${pnid.identification.email_token} \r\n\r\nYou may also enter the following 6-digit code on your console: ${pnid.identification.email_code}` + }; + + await sendMail(options); +} + +export async function sendEmailConfirmedEmail(pnid: mongoose.HydratedDocument): Promise { + const options: MailerOptions = { + to: pnid.email.address, + subject: '[Pretendo Network] Email address confirmed', + username: pnid.username, + paragraph: 'your email address has been confirmed. We hope you have fun on Pretendo Network!', + text: `Dear ${pnid.username}, \r\n\r\nYour email address has been confirmed. We hope you have fun on Pretendo Network!` + }; + + await sendMail(options); +} + +export async function sendForgotPasswordEmail(pnid: mongoose.HydratedDocument): Promise { + const tokenOptions: TokenOptions = { + system_type: 0xF, // * API + token_type: 0x5, // * Password reset + pid: pnid.pid, + access_level: pnid.access_level, + title_id: BigInt(0), + expire_time: BigInt(Date.now() + (24 * 60 * 60 * 1000)) // Only valid for 24 hours + }; + + const tokenBuffer: Buffer | null = await generateToken(config.aes_key, tokenOptions); + const passwordResetToken: string = tokenBuffer ? tokenBuffer.toString('hex') : ''; + + // TODO - Handle null token + + const mailerOptions: MailerOptions = { + to: pnid.email.address, + subject: '[Pretendo Network] Forgot Password', + username: pnid.username, + paragraph: 'a password reset has been requested from this account. If you did not request the password reset, please ignore this email. If you did request this password reset, please click the link below to reset your password.', + link: { + text: 'Reset password', + href: `${config.website_base}/account/reset-password?token=${encodeURIComponent(passwordResetToken)}` + }, + text: `Dear ${pnid.username}, a password reset has been requested from this account. \r\n\r\nIf you did not request the password reset, please ignore this email. \r\nIf you did request this password reset, please click the link to reset your password: ${config.website_base}/account/reset-password?token=${encodeURIComponent(passwordResetToken)}` + }; + + await sendMail(mailerOptions); +} + +export function makeSafeQs(query: ParsedQs): SafeQs { + const entries = Object.entries(query); + const output: SafeQs = {}; + + for (const [key, value] of entries) { + if (typeof value !== 'string') { + // * ignore non-strings + continue; + } + + output[key] = value; + } + + return output; +} + +export function getValueFromQueryString(qs: ParsedQs, key: string): string | undefined { + let property: string | ParsedQs | string[] | ParsedQs[] | SafeQs | undefined = qs[key]; + let value: string | undefined; + + if (property) { + if (Array.isArray(property)) { + property = property[0]; + } + + if (typeof property !== 'string') { + property = makeSafeQs(property); + value = (property)[key]; + } else { + value = property; + } + } + + return value; +} + +export function getValueFromHeaders(headers: IncomingHttpHeaders, key: string): string | undefined { + let header: string | string[] | undefined = headers[key]; + let value: string | undefined; + + if (header) { + if (Array.isArray(header)) { + header = header[0]; + } + + value = header; + } + + return value; +} + +export function mapToObject(map: Map): object { + return Object.fromEntries(Array.from(map.entries(), ([ k, v ]) => v instanceof Map ? [ k, mapToObject(v) ] : [ k, v ])); +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..cdef6fa --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "strict": true, + "resolveJsonModule": true, + "module": "commonjs", + "esModuleInterop": true, + "moduleResolution": "node", + "baseUrl": "src", + "outDir": "dist", + "allowJs": true, + "target": "es2022", + "noEmitOnError": true, + "paths": { + "@/*": ["./*"] + }, + "typeRoots": [ + "node_modules/@types", + "node_modules/@hcaptcha" + ] + }, + "include": ["src"] +} \ No newline at end of file