diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..e632239d --- /dev/null +++ b/.eslintignore @@ -0,0 +1,9 @@ +lib/ +es/ +es6/ +*.d.ts +src/**/*.js +test/**/*.js +.eslintrc.js +jest.config.js +coverage diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..6af638c1 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,123 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + sourceType: 'module', + warnOnUnsupportedTypeScriptVersion: false, + }, + extends: [ + 'airbnb-typescript/base', + 'plugin:@typescript-eslint/recommended', + ], + env: { + node: true, + }, + rules: { + 'arrow-parens': ['error', 'as-needed', { + requireForBlockBody: false, + }], + 'class-methods-use-this': 'off', + 'constructor-super': 'error', + 'handle-callback-err': 'error', + 'no-await-in-loop': 'off', + 'no-class-assign': 'error', + 'no-continue': 'off', + 'import/no-cycle': 'off', + 'no-mixed-operators': ['error', { + allowSamePrecedence: true, + }], + 'no-mixed-requires': 'error', + 'no-new-require': 'error', + 'no-path-concat': 'error', + 'no-restricted-syntax': ['error', + 'ForInStatement', + 'LabeledStatement', + 'WithStatement', + ], + 'no-this-before-super': 'error', + 'no-underscore-dangle': 'off', + 'prefer-destructuring': ['error', { + 'VariableDeclarator': { + array: false, + object: true, + }, + 'AssignmentExpression': { + array: false, + object: true, + } + }, { + enforceForRenamedProperties: false, + }], + 'strict': ['error', 'global'], + 'template-curly-spacing': ['error', 'always'], + + 'import/named': 'error', + 'import/prefer-default-export': 'off', + + '@typescript-eslint/array-type': ['error', { + default: 'generic', + }], + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/explicit-function-return-type': ['error', { + allowExpressions: true, + }], + '@typescript-eslint/explicit-member-accessibility': ['error', { + 'overrides': { + constructors: 'no-public', + } + }], + '@typescript-eslint/explicit-module-boundary-types': ['error', { + allowArgumentsExplicitlyTypedAsAny: true, + }], + '@typescript-eslint/indent': ['error', 4, { + SwitchCase: 1, + VariableDeclarator: 1, + outerIIFEBody: 1, + MemberExpression: 0, + FunctionDeclaration: { + parameters: 1, + body: 1, + }, + FunctionExpression: { + parameters: 1, + body: 1, + }, + CallExpression: { + arguments: 1, + }, + ArrayExpression: 1, + ObjectExpression: 1, + ImportDeclaration: 1, + flatTernaryExpressions: false, + ignoredNodes: [ + 'JSXExpressionContainer > ConditionalExpression' + ], + ignoreComments: false, + }], + '@typescript-eslint/lines-between-class-members': ['error', 'always', { + exceptAfterSingleLine: true, + }], + '@typescript-eslint/naming-convention': [ + "error", + { + selector: ['enum'], + format: ['UPPER_CASE'], + } + ], + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-parameter-properties': 'off', + '@typescript-eslint/no-this-alias': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + '@typescript-eslint/no-unused-vars': ['error', { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: true, + }], + '@typescript-eslint/no-use-before-define': ['error', 'nofunc'], + '@typescript-eslint/object-curly-spacing': ['error', 'never'], + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/restrict-plus-operands': 'error', + '@typescript-eslint/unbound-method': 'error', + }, +}; diff --git a/package-lock.json b/package-lock.json index 456f5c45..b03a0678 100644 --- a/package-lock.json +++ b/package-lock.json @@ -383,6 +383,89 @@ "@cspotcode/source-map-consumer": "0.8.0" } }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.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 + }, + "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 + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "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/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -453,6 +536,32 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@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" + } + }, "@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -587,6 +696,18 @@ "@types/range-parser": "*" } }, + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -661,6 +782,228 @@ "@types/superagent": "*" } }, + "@typescript-eslint/eslint-plugin": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.3.tgz", + "integrity": "sha512-tBgfA3K/3TsZY46ROGvoRxQr1wBkclbVqRQep97MjVHJzcRBURRY3sNFqLk0/Xr//BY5hM9H2p/kp+6qim85SA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.29.3", + "@typescript-eslint/scope-manager": "4.29.3", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.3.tgz", + "integrity": "sha512-ffIvbytTVWz+3keg+Sy94FG1QeOvmV9dP2YSdLFHw/ieLXWCa3U1TYu8IRCOpMv2/SPS8XqhM1+ou1YHsdzKrg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.29.3", + "@typescript-eslint/types": "4.29.3", + "@typescript-eslint/typescript-estree": "4.29.3", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.29.3.tgz", + "integrity": "sha512-jrHOV5g2u8ROghmspKoW7pN8T/qUzk0+DITun0MELptvngtMrwUJ1tv5zMI04CYVEUsSrN4jV7AKSv+I0y0EfQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.29.3", + "@typescript-eslint/types": "4.29.3", + "@typescript-eslint/typescript-estree": "4.29.3", + "debug": "^4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "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": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.29.3.tgz", + "integrity": "sha512-x+w8BLXO7iWPkG5mEy9bA1iFRnk36p/goVlYobVWHyDw69YmaH9q6eA+Fgl7kYHmFvWlebUTUfhtIg4zbbl8PA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.29.3", + "@typescript-eslint/visitor-keys": "4.29.3" + } + }, + "@typescript-eslint/types": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.29.3.tgz", + "integrity": "sha512-s1eV1lKNgoIYLAl1JUba8NhULmf+jOmmeFO1G5MN/RBCyyzg4TIOfIOICVNC06lor+Xmy4FypIIhFiJXOknhIg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.3.tgz", + "integrity": "sha512-45oQJA0bxna4O5TMwz55/TpgjX1YrAPOI/rb6kPgmdnemRZx/dB0rsx+Ku8jpDvqTxcE1C/qEbVHbS3h0hflag==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.29.3", + "@typescript-eslint/visitor-keys": "4.29.3", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.3.tgz", + "integrity": "sha512-MGGfJvXT4asUTeVs0Q2m+sY63UsfnA+C/FDgBKV3itLBmM9H0u+URcneePtkd0at1YELmZK6HSolCqM4Fzs6yA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.29.3", + "eslint-visitor-keys": "^2.0.0" + } + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -676,6 +1019,18 @@ "negotiator": "0.6.2" } }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, "acorn-walk": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", @@ -692,6 +1047,24 @@ "indent-string": "^4.0.0" } }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-cyan": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", @@ -796,12 +1169,42 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + } + }, + "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 + }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -814,6 +1217,12 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "async": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/async/-/async-3.2.1.tgz", @@ -1068,6 +1477,12 @@ "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 + }, "caniuse-lite": { "version": "1.0.30001252", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", @@ -1277,12 +1692,6 @@ "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", "dev": true }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1316,6 +1725,12 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "dev": true + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -1448,6 +1863,12 @@ "type-detect": "^4.0.0" } }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, "default-require-extensions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", @@ -1465,6 +1886,15 @@ } } }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -1540,6 +1970,32 @@ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, + "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" + }, + "dependencies": { + "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 + } + } + }, + "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" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1569,38 +2025,502 @@ "os-family": "^1.0.0" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", + "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "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" + } + }, + "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" + } + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + }, + "globals": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "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" + } + }, + "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 + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "eslint-config-airbnb": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz", + "integrity": "sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^14.2.1", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + }, + "eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + }, + "eslint-config-airbnb-typescript": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-12.3.1.tgz", + "integrity": "sha512-ql/Pe6/hppYuRp4m3iPaHJqkBB7dgeEmGPQ6X0UNmrQOfTF+dXw29/ZjU2kQ6RDoLxaxOA+Xqv07Vbef6oVTWw==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "^4.4.1", + "eslint-config-airbnb": "^18.2.0", + "eslint-config-airbnb-base": "^14.2.0" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-module-utils": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz", + "integrity": "sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.24.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", + "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.6.2", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.6.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.4", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.11.0" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, + "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" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "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 }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "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.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "etag": { @@ -1838,12 +2758,97 @@ } } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "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": { + "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" + } + }, + "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" + } + }, + "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 + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "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" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fast-safe-stringify": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz", "integrity": "sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag==", "dev": true }, + "fastq": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", + "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -1853,6 +2858,15 @@ "escape-string-regexp": "^1.0.5" } }, + "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" + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1901,12 +2915,73 @@ "pkg-dir": "^4.1.0" } }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + }, + "dependencies": { + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, "flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, + "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.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -1977,6 +3052,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2034,12 +3115,43 @@ "path-is-absolute": "^1.0.0" } }, + "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" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, "graceful-fs": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", @@ -2070,6 +3182,33 @@ "ansi-regex": "^2.0.0" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "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.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -2146,6 +3285,12 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -2178,6 +3323,30 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "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" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2333,11 +3502,21 @@ } } }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "inversify": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.1.1.tgz", - "integrity": "sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ==", - "dev": true + "integrity": "sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ==" }, "ipaddr.js": { "version": "1.9.0", @@ -2353,12 +3532,43 @@ "kind-of": "^3.0.2" } }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "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 }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, "is-ci": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", @@ -2368,6 +3578,15 @@ "ci-info": "^1.5.0" } }, + "is-core-module": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", + "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -2377,6 +3596,15 @@ "kind-of": "^3.0.2" } }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -2423,6 +3651,12 @@ "is-extglob": "^2.1.1" } }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -2432,6 +3666,15 @@ "kind-of": "^3.0.2" } }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -2455,12 +3698,40 @@ } } }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -2649,6 +3920,24 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "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": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -2681,6 +3970,28 @@ "is-buffer": "^1.1.5" } }, + "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" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2696,6 +4007,12 @@ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -2708,12 +4025,24 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "lodash.toarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", "dev": true }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, "log-update": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", @@ -2782,6 +4111,12 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "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", @@ -3202,12 +4537,12 @@ } }, "moq.ts": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/moq.ts/-/moq.ts-7.3.4.tgz", - "integrity": "sha512-rOelPx7ns+RzyliPwHH2wFVqGowITKRQtECLvVKhiIt4oM4r2hYq4AmgQh0NpWlMxWl2fuZtPJhikrhVpAoF5g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/moq.ts/-/moq.ts-3.0.0.tgz", + "integrity": "sha512-42MjOerHoc7wDeg7FYdQ6v2xCLHj/7QyOsI8ee9zrqRP5BzwxJT/JfUKF6Mn5k66MaN1QveHyZZpeyVIM5OQqw==", "dev": true, "requires": { - "tslib": "2.1.0" + "tslib": "^1.9.0" } }, "ms": { @@ -3285,6 +4620,12 @@ } } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -3338,6 +4679,18 @@ "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", "dev": true }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -3591,6 +4944,12 @@ "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", "dev": true }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -3608,6 +4967,29 @@ } } }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", + "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -3625,6 +5007,17 @@ } } }, + "object.values": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -3642,6 +5035,20 @@ "wrappy": "1" } }, + "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" + } + }, "os-family": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/os-family/-/os-family-1.1.0.tgz", @@ -3715,6 +5122,25 @@ "release-zalgo": "^1.0.0" } }, + "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-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3726,6 +5152,12 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -3749,6 +5181,15 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, "pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -3761,6 +5202,12 @@ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", @@ -3830,6 +5277,15 @@ } } }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, "plugin-error": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", @@ -3867,15 +5323,11 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, - "prettyjson": { + "prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.1.tgz", - "integrity": "sha1-/P+rQdGcq0365eV15kJGYZsS0ok=", - "dev": true, - "requires": { - "colors": "^1.1.2", - "minimist": "^1.2.0" - } + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true }, "process-on-spawn": { "version": "1.0.0", @@ -3886,6 +5338,12 @@ "fromentries": "^1.2.0" } }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "promisify-event": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/promisify-event/-/promisify-event-1.0.0.tgz", @@ -3986,6 +5444,12 @@ } } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, "qs": { "version": "6.10.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", @@ -3995,6 +5459,12 @@ "side-channel": "^1.0.4" } }, + "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 + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -4032,6 +5502,27 @@ } } }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -4069,6 +5560,12 @@ } } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -4096,6 +5593,12 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "resolve": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", @@ -4141,6 +5644,12 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4156,6 +5665,15 @@ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true }, + "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" + } + }, "run-sequence": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz", @@ -4369,6 +5887,40 @@ } } }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + } + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -4545,6 +6097,38 @@ } } }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz", + "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", + "dev": true + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -4634,6 +6218,26 @@ } } }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -4652,6 +6256,12 @@ "ansi-regex": "^2.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4751,6 +6361,72 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -4762,6 +6438,12 @@ "minimatch": "^3.0.4" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -4891,10 +6573,33 @@ } } }, + "tsconfig-paths": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz", + "integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "tslint": { @@ -5014,6 +6719,15 @@ } } }, + "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" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5050,6 +6764,18 @@ "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", "dev": true }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -5133,6 +6859,15 @@ "integrity": "sha512-cC/jeGLoeMiu0NteTQsFZTQ9p1aLYs9uODV3HbS3Zx7fAk+dY0GsrUCC8C153szTH3X9NkPtYp0FpLLS2qIKMw==", "dev": true }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -5162,6 +6897,22 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -5176,6 +6927,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -5185,6 +6949,12 @@ "string-width": "^1.0.2 || 2" } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index e8ffdc3a..127b3d32 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build:es6": "tsc -p src/tsconfig-es6.json", "clean": "rm -r es es6 lib", "test": "nyc --require ts-node/register mocha test/**/*.test.ts --reporter spec --retries 3 --project --require 'node_modules/reflect-metadata/Reflect.js' --exit", - "pretest": "tslint --project .", + "pretest": "eslint ./", "publish-please": "publish-please", "prepublish": "publish-please guard", "update": "updates --update && npm install", @@ -39,35 +39,38 @@ "@types/body-parser": "1.19.1", "@types/chai": "4.2.21", "@types/cookie-parser": "1.4.2", - "@types/express": "^4.17.13", + "@types/express": "4.17.13", "@types/mocha": "9.0.0", - "@types/node": "^16.7.6", - "@types/prettyjson": "0.0.30", + "@types/node": "16.7.6", "@types/sinon": "10.0.2", "@types/supertest": "2.0.11", + "@typescript-eslint/eslint-plugin": "4.29.3", + "@typescript-eslint/parser": "4.29.3", "async": "3.2.1", "bluebird": "3.7.2", "body-parser": "1.19.0", "chai": "4.3.4", "cookie-parser": "1.4.5", - "inversify": "5.1.1", + "eslint": "7.32.0", + "eslint-config-airbnb-typescript": "12.3.1", + "eslint-plugin-import": "2.24.2", "mocha": "9.1.1", - "moq.ts": "^7.3.4", - "nyc": "^15.1.0", - "prettyjson": "1.2.1", + "moq.ts": "3.0.0", + "nyc": "15.1.0", "publish-please": "5.5.2", "reflect-metadata": "0.1.13", "run-sequence": "2.2.1", "sinon": "11.1.2", "source-map-support": "0.5.19", "supertest": "6.1.6", - "ts-node": "^10.2.1", + "ts-node": "10.2.1", "tslint": "6.1.3", - "typescript": "^4.4.2", - "updates": "^12.1.0" + "typescript": "4.4.2", + "updates": "12.1.0" }, "dependencies": { "express": "4.17.1", - "http-status-codes": "^2.1.4" + "http-status-codes": "2.1.4", + "inversify": "5.1.1" } } diff --git a/src/base_http_controller.ts b/src/base_http_controller.ts index 425d1cfd..5cdb9b40 100644 --- a/src/base_http_controller.ts +++ b/src/base_http_controller.ts @@ -1,8 +1,8 @@ -import { injectable } from "inversify"; -import { injectHttpContext } from "./decorators"; -import { interfaces } from "./interfaces"; -import { HttpResponseMessage } from "./httpResponseMessage"; -import { URL } from "url"; +import {injectable} from 'inversify'; +import {URL} from 'url'; +import {StatusCodes} from 'http-status-codes'; +import {injectHttpContext} from './decorators'; +import {HttpResponseMessage} from './httpResponseMessage'; import { CreatedNegotiatedContentResult, ConflictResult, @@ -16,61 +16,61 @@ import { RedirectResult, ResponseMessageResult, StatusCodeResult, - JsonResult -} from "./results"; -import { OK } from "http-status-codes"; + JsonResult, +} from './results'; +import {HttpContext} from './interfaces'; @injectable() export class BaseHttpController { - @injectHttpContext protected readonly httpContext!: interfaces.HttpContext; + @injectHttpContext protected readonly httpContext!: HttpContext; - protected created(location: string | URL, content: T) { + protected created(location: string | URL, content: T): CreatedNegotiatedContentResult { return new CreatedNegotiatedContentResult(location, content); } - protected conflict() { + protected conflict(): ConflictResult { return new ConflictResult(); } protected ok(content: T): OkNegotiatedContentResult; protected ok(): OkResult; - protected ok(content?: T) { - return content === undefined ? - new OkResult() : - new OkNegotiatedContentResult(content); + protected ok(content?: T): OkResult { + return content === undefined + ? new OkResult() + : new OkNegotiatedContentResult(content); } protected badRequest(): BadRequestResult; protected badRequest(message: string): BadRequestErrorMessageResult; - protected badRequest(message?: string) { - return message === undefined ? - new BadRequestResult() : - new BadRequestErrorMessageResult(message); + protected badRequest(message?: string): BadRequestResult { + return message === undefined + ? new BadRequestResult() + : new BadRequestErrorMessageResult(message); } protected internalServerError(): InternalServerErrorResult; protected internalServerError(error: Error): ExceptionResult; - protected internalServerError(error?: Error) { + protected internalServerError(error?: Error): InternalServerErrorResult { return error ? new ExceptionResult(error) : new InternalServerErrorResult(); } - protected notFound() { + protected notFound(): NotFoundResult { return new NotFoundResult(); } - protected redirect(uri: string | URL) { + protected redirect(uri: string | URL): RedirectResult { return new RedirectResult(uri); } - protected responseMessage(message: HttpResponseMessage) { + protected responseMessage(message: HttpResponseMessage): ResponseMessageResult { return new ResponseMessageResult(message); } - protected statusCode(statusCode: number) { + protected statusCode(statusCode: number): StatusCodeResult { return new StatusCodeResult(statusCode); } - protected json(content: any, statusCode: number = OK) { + protected json(content: any, statusCode: number = StatusCodes.OK): JsonResult { return new JsonResult(content, statusCode); } } diff --git a/src/base_middleware.ts b/src/base_middleware.ts index 13a493f3..92eb90b2 100644 --- a/src/base_middleware.ts +++ b/src/base_middleware.ts @@ -1,14 +1,16 @@ -import * as express from "express"; -import { injectable, interfaces as inversifyInterfaces } from "inversify"; -import { interfaces } from "./interfaces"; +import * as express from 'express'; +import {injectable, interfaces as inversifyInterfaces} from 'inversify'; +import * as interfaces from './interfaces'; @injectable() export abstract class BaseMiddleware implements BaseMiddleware { // httpContext is initialized when the middleware is invoked // see resolveMidleware in server.ts for more details - protected readonly httpContext!: interfaces.HttpContext; + public httpContext!: interfaces.HttpContext; - protected bind(serviceIdentifier: inversifyInterfaces.ServiceIdentifier): inversifyInterfaces.BindingToSyntax { + protected bind( + serviceIdentifier: inversifyInterfaces.ServiceIdentifier, + ): inversifyInterfaces.BindingToSyntax { return this.httpContext.container.bind(serviceIdentifier); } diff --git a/src/constants.ts b/src/constants.ts index 1ab266ce..361338f6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,14 +1,14 @@ export const TYPE = { - AuthProvider: Symbol.for("AuthProvider"), - Controller: Symbol.for("Controller"), - HttpContext: Symbol.for("HttpContext") + AuthProvider: Symbol.for('AuthProvider'), + Controller: Symbol.for('Controller'), + HttpContext: Symbol.for('HttpContext'), }; export const METADATA_KEY = { - controller: "inversify-express-utils:controller", - controllerMethod: "inversify-express-utils:controller-method", - controllerParameter: "inversify-express-utils:controller-parameter", - httpContext: "inversify-express-utils:httpcontext" + controller: 'inversify-express-utils:controller', + controllerMethod: 'inversify-express-utils:controller-method', + controllerParameter: 'inversify-express-utils:controller-parameter', + httpContext: 'inversify-express-utils:httpcontext', }; export enum PARAMETER_TYPE { @@ -20,27 +20,25 @@ export enum PARAMETER_TYPE { HEADERS, COOKIES, NEXT, - PRINCIPAL + PRINCIPAL, } export enum HTTP_VERBS_ENUM { - all = "ALL", - connect = "CONNECT", - delete = "DELETE", - get = "GET", - head = "HEAD", - options = "OPTIONS", - patch = "PATCH", - post = "POST", - propfind = "PROPFIND", - put = "PUT", - trace = "TRACE" + all = 'ALL', + connect = 'CONNECT', + delete = 'DELETE', + get = 'GET', + head = 'HEAD', + options = 'OPTIONS', + patch = 'PATCH', + post = 'POST', + propfind = 'PROPFIND', + put = 'PUT', + trace = 'TRACE', } -export const DUPLICATED_CONTROLLER_NAME = (name: string) => - `Two controllers cannot have the same name: ${name}`; +export const DUPLICATED_CONTROLLER_NAME = (name: string): string => `Two controllers cannot have the same name: ${ name }`; -export const NO_CONTROLLERS_FOUND = "No controllers have been found! " + - "Please ensure that you have register at least one Controller."; +export const NO_CONTROLLERS_FOUND = 'No controllers have been found! Please ensure that you have register at least one Controller.'; -export const DEFAULT_ROUTING_ROOT_PATH = "/"; +export const DEFAULT_ROUTING_ROOT_PATH = '/'; diff --git a/src/content/httpContent.ts b/src/content/httpContent.ts index 1c1a3e84..90816d66 100644 --- a/src/content/httpContent.ts +++ b/src/content/httpContent.ts @@ -1,11 +1,11 @@ -import { OutgoingHttpHeaders } from "http"; +import {OutgoingHttpHeaders} from 'http'; export abstract class HttpContent { - private _headers: OutgoingHttpHeaders = {}; + private _headers: OutgoingHttpHeaders = {}; - public get headers() { - return this._headers; - } + public get headers(): OutgoingHttpHeaders { + return this._headers; + } - public abstract readAsStringAsync(): Promise; + public abstract readAsStringAsync(): Promise; } diff --git a/src/content/jsonContent.ts b/src/content/jsonContent.ts index 2c7b6ab4..a376cb47 100644 --- a/src/content/jsonContent.ts +++ b/src/content/jsonContent.ts @@ -1,19 +1,19 @@ -import { HttpContent } from "./httpContent"; +import {HttpContent} from './httpContent'; -const DEFAULT_MEDIA_TYPE = "application/json"; +const DEFAULT_MEDIA_TYPE = 'application/json'; export class JsonContent extends HttpContent { - private content: string; + private content: string; - constructor(content: any) { - super(); + constructor(content: any) { + super(); - this.content = JSON.stringify(content); + this.content = JSON.stringify(content); - this.headers["content-type"] = DEFAULT_MEDIA_TYPE; - } + this.headers['content-type'] = DEFAULT_MEDIA_TYPE; + } - public readAsStringAsync() { - return Promise.resolve(this.content); - } + public readAsStringAsync(): Promise { + return Promise.resolve(this.content); + } } diff --git a/src/content/stringContent.ts b/src/content/stringContent.ts index c98858af..15a4b0e0 100644 --- a/src/content/stringContent.ts +++ b/src/content/stringContent.ts @@ -1,15 +1,15 @@ -import { HttpContent } from "./httpContent"; +import {HttpContent} from './httpContent'; -const DEFAULT_MEDIA_TYPE = "text/plain"; +const DEFAULT_MEDIA_TYPE = 'text/plain'; export class StringContent extends HttpContent { - constructor(private content: string) { - super(); + constructor(private content: string) { + super(); - this.headers["content-type"] = DEFAULT_MEDIA_TYPE; - } + this.headers['content-type'] = DEFAULT_MEDIA_TYPE; + } - public readAsStringAsync() { - return Promise.resolve(this.content); - } + public readAsStringAsync(): Promise { + return Promise.resolve(this.content); + } } diff --git a/src/debug.ts b/src/debug.ts index 8fa17582..e11ac239 100644 --- a/src/debug.ts +++ b/src/debug.ts @@ -1,108 +1,98 @@ -import { interfaces as inversifyInterfaces } from "inversify"; -import { interfaces } from "./interfaces"; -import { PARAMETER_TYPE } from "./constants"; +import {interfaces as inversifyInterfaces} from 'inversify'; +import {PARAMETER_TYPE} from './constants'; +import {RouteDetails, RouteInfo, RawMetadata} from './interfaces'; import { getControllersFromContainer, getControllerMetadata, getControllerMethodMetadata, - getControllerParameterMetadata -} from "./utils"; - -export function getRouteInfo(container: inversifyInterfaces.Container): interfaces.RouteInfo[] { + getControllerParameterMetadata, +} from './utils'; +export function getRouteInfo( + container: inversifyInterfaces.Container, +): Array { const raw = getRawMetadata(container); return raw.map(r => { - const controllerId = r.controllerMetadata.target.name; const endpoints = r.methodMetadata.map(m => { - const method = m.method.toUpperCase(); const controllerPath = r.controllerMetadata.path; const actionPath = m.path; const paramMetadata = r.parameterMetadata; - let args: string[] | undefined = undefined; + let args: Array | undefined; if (paramMetadata !== undefined) { const paramMetadataForKey = paramMetadata[m.key] || undefined; if (paramMetadataForKey) { args = (r.parameterMetadata[m.key] || []).map(a => { - let type = ""; + let type = ''; switch (a.type) { case PARAMETER_TYPE.RESPONSE: - type = "@response"; + type = '@response'; break; case PARAMETER_TYPE.REQUEST: - type = "@request"; + type = '@request'; break; case PARAMETER_TYPE.NEXT: - type = "@next"; + type = '@next'; break; case PARAMETER_TYPE.PARAMS: - type = "@requestParam"; + type = '@requestParam'; break; case PARAMETER_TYPE.QUERY: - type = "queryParam"; + type = 'queryParam'; break; case PARAMETER_TYPE.BODY: - type = "@requestBody"; + type = '@requestBody'; break; case PARAMETER_TYPE.HEADERS: - type = "@requestHeaders"; + type = '@requestHeaders'; break; case PARAMETER_TYPE.COOKIES: - type = "@cookies"; + type = '@cookies'; break; case PARAMETER_TYPE.PRINCIPAL: - type = "@principal"; + type = '@principal'; + break; + default: break; } - return `${type} ${a.parameterName}`; + + return `${ type } ${ a.parameterName }`; }); } } - const details: interfaces.RouteDetails = { - route: `${method} ${controllerPath}${actionPath}` + const details: RouteDetails = { + route: `${ method } ${ controllerPath }${ actionPath }`, }; if (args) { - details["args"] = args; + details.args = args; } return details; - }); return { controller: controllerId, - endpoints: endpoints + endpoints, }; - }); - } -export function getRawMetadata(container: inversifyInterfaces.Container) { - +export function getRawMetadata(container: inversifyInterfaces.Container): Array { const controllers = getControllersFromContainer(container, true); - return controllers.map((controller) => { - - let constructor = controller.constructor; - let controllerMetadata: interfaces.ControllerMetadata = getControllerMetadata(constructor); - let methodMetadata: interfaces.ControllerMethodMetadata[] = getControllerMethodMetadata(constructor); - let parameterMetadata: interfaces.ControllerParameterMetadata = getControllerParameterMetadata(constructor); + return controllers.map(controller => { + const {constructor} = controller; return { - controllerMetadata, - methodMetadata, - parameterMetadata + controllerMetadata: getControllerMetadata(constructor), + methodMetadata: getControllerMethodMetadata(constructor), + parameterMetadata: getControllerParameterMetadata(constructor), }; - }); - } - - diff --git a/src/decorators.ts b/src/decorators.ts index 0a18910c..a9decb74 100644 --- a/src/decorators.ts +++ b/src/decorators.ts @@ -1,16 +1,28 @@ -import { inject, injectable, decorate } from "inversify"; -import { interfaces } from "./interfaces"; -import { TYPE, METADATA_KEY, PARAMETER_TYPE, HTTP_VERBS_ENUM } from "./constants"; +import {inject, injectable, decorate} from 'inversify'; +import { + Controller, + Middleware, + ControllerMetadata, + HandlerDecorator, + ControllerMethodMetadata, + ControllerParameterMetadata, + ParameterMetadata, +} from './interfaces'; +import { + TYPE, + METADATA_KEY, + PARAMETER_TYPE, + HTTP_VERBS_ENUM, +} from './constants'; export const injectHttpContext = inject(TYPE.HttpContext); -export function controller(path: string, ...middleware: interfaces.Middleware[]) { - return function (target: any) { - - let currentMetadata: interfaces.ControllerMetadata = { - middleware: middleware, - path: path, - target: target +export function controller(path: string, ...middleware: Array) { + return (target: any): void => { + const currentMetadata: ControllerMetadata = { + middleware, + path, + target, }; decorate(injectable(), target); @@ -22,9 +34,9 @@ export function controller(path: string, ...middleware: interfaces.Middleware[]) // We attach metadata to the Reflect object itself to avoid // declaring additonal globals. Also, the Reflect is avaiable // in both node and web browsers. - const previousMetadata: interfaces.ControllerMetadata[] = Reflect.getMetadata( + const previousMetadata: Array = Reflect.getMetadata( METADATA_KEY.controller, - Reflect + Reflect, ) || []; const newMetadata = [currentMetadata, ...previousMetadata]; @@ -32,56 +44,54 @@ export function controller(path: string, ...middleware: interfaces.Middleware[]) Reflect.defineMetadata( METADATA_KEY.controller, newMetadata, - Reflect + Reflect, ); - }; } -export function all(path: string, ...middleware: interfaces.Middleware[]): interfaces.HandlerDecorator { - return httpMethod("all", path, ...middleware); +export function all(path: string, ...middleware: Array): HandlerDecorator { + return httpMethod('all', path, ...middleware); } -export function httpGet(path: string, ...middleware: interfaces.Middleware[]): interfaces.HandlerDecorator { - return httpMethod("get", path, ...middleware); +export function httpGet(path: string, ...middleware: Array): HandlerDecorator { + return httpMethod('get', path, ...middleware); } -export function httpPost(path: string, ...middleware: interfaces.Middleware[]): interfaces.HandlerDecorator { - return httpMethod("post", path, ...middleware); +export function httpPost(path: string, ...middleware: Array): HandlerDecorator { + return httpMethod('post', path, ...middleware); } -export function httpPut(path: string, ...middleware: interfaces.Middleware[]): interfaces.HandlerDecorator { - return httpMethod("put", path, ...middleware); +export function httpPut(path: string, ...middleware: Array): HandlerDecorator { + return httpMethod('put', path, ...middleware); } -export function httpPatch(path: string, ...middleware: interfaces.Middleware[]): interfaces.HandlerDecorator { - return httpMethod("patch", path, ...middleware); +export function httpPatch(path: string, ...middleware: Array): HandlerDecorator { + return httpMethod('patch', path, ...middleware); } -export function httpHead(path: string, ...middleware: interfaces.Middleware[]): interfaces.HandlerDecorator { - return httpMethod("head", path, ...middleware); +export function httpHead(path: string, ...middleware: Array): HandlerDecorator { + return httpMethod('head', path, ...middleware); } -export function httpDelete(path: string, ...middleware: interfaces.Middleware[]): interfaces.HandlerDecorator { - return httpMethod("delete", path, ...middleware); +export function httpDelete(path: string, ...middleware: Array): HandlerDecorator { + return httpMethod('delete', path, ...middleware); } export function httpMethod( method: keyof typeof HTTP_VERBS_ENUM, path: string, - ...middleware: interfaces.Middleware[] -): interfaces.HandlerDecorator { - return function (target: any, key: string, value: any) { - - let metadata: interfaces.ControllerMethodMetadata = { + ...middleware: Array +): HandlerDecorator { + return (target: any, key: string): void => { + const metadata: ControllerMethodMetadata = { key, method, middleware, path, - target + target, }; - let metadataList: interfaces.ControllerMethodMetadata[] = []; + let metadataList: Array = []; if (!Reflect.hasMetadata(METADATA_KEY.controllerMethod, target.constructor)) { Reflect.defineMetadata(METADATA_KEY.controllerMethod, metadataList, target.constructor); @@ -95,37 +105,47 @@ export function httpMethod( export const request: () => ParameterDecorator = paramDecoratorFactory(PARAMETER_TYPE.REQUEST); export const response: () => ParameterDecorator = paramDecoratorFactory(PARAMETER_TYPE.RESPONSE); -export const requestParam: (paramName?: string) => ParameterDecorator = paramDecoratorFactory(PARAMETER_TYPE.PARAMS); -export const queryParam: (queryParamName?: string) => ParameterDecorator = paramDecoratorFactory(PARAMETER_TYPE.QUERY); +export const requestParam: (paramName?: string) => ParameterDecorator = paramDecoratorFactory( + PARAMETER_TYPE.PARAMS, +); +export const queryParam: (queryParamName?: string) => ParameterDecorator = paramDecoratorFactory( + PARAMETER_TYPE.QUERY, +); export const requestBody: () => ParameterDecorator = paramDecoratorFactory(PARAMETER_TYPE.BODY); -export const requestHeaders: (headerName?: string) => ParameterDecorator = paramDecoratorFactory(PARAMETER_TYPE.HEADERS); -export const cookies: (cookieName?: string) => ParameterDecorator = paramDecoratorFactory(PARAMETER_TYPE.COOKIES); +export const requestHeaders: (headerName?: string) => ParameterDecorator = paramDecoratorFactory( + PARAMETER_TYPE.HEADERS, +); +export const cookies: (cookieName?: string) => ParameterDecorator = paramDecoratorFactory( + PARAMETER_TYPE.COOKIES, +); export const next: () => ParameterDecorator = paramDecoratorFactory(PARAMETER_TYPE.NEXT); export const principal: () => ParameterDecorator = paramDecoratorFactory(PARAMETER_TYPE.PRINCIPAL); -function paramDecoratorFactory(parameterType: PARAMETER_TYPE): (name?: string) => ParameterDecorator { - return function (name?: string): ParameterDecorator { - return params(parameterType, name); - }; +function paramDecoratorFactory( + parameterType: PARAMETER_TYPE, +): (name?: string) => ParameterDecorator { + return (name?: string): ParameterDecorator => params(parameterType, name); } export function params(type: PARAMETER_TYPE, parameterName?: string) { - return function (target: Object, methodName: string | symbol, index: number) { - - let metadataList: interfaces.ControllerParameterMetadata = {}; - let parameterMetadataList: interfaces.ParameterMetadata[] = []; - let parameterMetadata: interfaces.ParameterMetadata = { - index: index, + return (target: Controller, methodName: string | symbol, index: number): void => { + let metadataList: ControllerParameterMetadata = {}; + let parameterMetadataList: Array = []; + const parameterMetadata: ParameterMetadata = { + index, injectRoot: parameterName === undefined, - parameterName: parameterName, - type: type + parameterName, + type, }; if (!Reflect.hasMetadata(METADATA_KEY.controllerParameter, target.constructor)) { parameterMetadataList.unshift(parameterMetadata); } else { - metadataList = Reflect.getMetadata(METADATA_KEY.controllerParameter, target.constructor); - if (metadataList.hasOwnProperty(methodName)) { - parameterMetadataList = metadataList[methodName as string] as interfaces.ParameterMetadata[]; + metadataList = Reflect.getMetadata( + METADATA_KEY.controllerParameter, + target.constructor, + ); + if (metadataList[methodName as string]) { + parameterMetadataList = metadataList[methodName as string] || []; } parameterMetadataList.unshift(parameterMetadata); } diff --git a/src/httpResponseMessage.ts b/src/httpResponseMessage.ts index 4304ad6d..54c52eda 100644 --- a/src/httpResponseMessage.ts +++ b/src/httpResponseMessage.ts @@ -1,5 +1,5 @@ -import { OutgoingHttpHeaders } from "http"; -import { HttpContent } from "./content/httpContent"; +import {OutgoingHttpHeaders} from 'http'; +import {HttpContent} from './content/httpContent'; export class HttpResponseMessage { private _content!: HttpContent; @@ -14,7 +14,7 @@ export class HttpResponseMessage { this._headers = headers; } - public get content() { + public get content(): HttpContent { return this._content; } @@ -30,7 +30,7 @@ export class HttpResponseMessage { public set statusCode(code: number) { if (code < 0 || code > 999) { - throw new Error(`${code} is not a valid status code`); + throw new Error(`${ code } is not a valid status code`); } this._statusCode = code; diff --git a/src/index.ts b/src/index.ts index 41dc8393..30d08038 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,54 +1,13 @@ -import "reflect-metadata"; - -import { InversifyExpressServer } from "./server"; -import { - controller, httpMethod, httpGet, httpPut, httpPost, httpPatch, - httpHead, all, httpDelete, request, response, requestParam, queryParam, - requestBody, requestHeaders, cookies, next, principal, injectHttpContext -} from "./decorators"; -import { TYPE } from "./constants"; -import { interfaces } from "./interfaces"; -import * as results from "./results"; -import { BaseHttpController } from "./base_http_controller"; -import { BaseMiddleware } from "./base_middleware"; -import { cleanUpMetadata } from "./utils"; -import { getRouteInfo, getRawMetadata } from "./debug"; -import { HttpResponseMessage } from "./httpResponseMessage"; -import { StringContent } from "./content/stringContent"; -import { JsonContent } from "./content/jsonContent"; -import { HttpContent } from "./content/httpContent"; - -export { - getRouteInfo, - getRawMetadata, - cleanUpMetadata, - interfaces, - InversifyExpressServer, - controller, - httpMethod, - httpGet, - httpPut, - httpPost, - httpPatch, - httpHead, - all, - httpDelete, - TYPE, - request, - response, - requestParam, - queryParam, - requestBody, - requestHeaders, - cookies, - next, - principal, - BaseHttpController, - injectHttpContext, - BaseMiddleware, - HttpResponseMessage, - HttpContent, - StringContent, - JsonContent, - results -}; +export * from './server'; +export * from './decorators'; +export * from './constants'; +export * from './interfaces'; +export * from './results'; +export * from './base_http_controller'; +export * from './base_middleware'; +export * from './utils'; +export * from './debug'; +export * from './httpResponseMessage'; +export * from './content/stringContent'; +export * from './content/jsonContent'; +export * from './content/httpContent'; diff --git a/src/interfaces.ts b/src/interfaces.ts index 9a237bd3..37bf1572 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,85 +1,83 @@ -import * as express from "express"; -import { interfaces as inversifyInterfaces } from "inversify"; -import { HTTP_VERBS_ENUM, PARAMETER_TYPE } from "./constants"; -import { HttpResponseMessage } from "./httpResponseMessage"; - -namespace interfaces { - - export type Middleware = (inversifyInterfaces.ServiceIdentifier | express.RequestHandler); - - export interface ControllerMetadata { - path: string; - middleware: Middleware[]; - target: any; - } - - export interface ControllerMethodMetadata extends ControllerMetadata { - method: keyof typeof HTTP_VERBS_ENUM; - key: string; - } - - export interface ControllerParameterMetadata { - [methodName: string]: ParameterMetadata[]; - } - - export interface ParameterMetadata { - parameterName?: string; - injectRoot: boolean; - index: number; - type: PARAMETER_TYPE; - } - - export interface Controller { } - - export interface HandlerDecorator { - (target: any, key: string, value: any): void; - } - - export interface ConfigFunction { - (app: express.Application): void; - } - - export interface RoutingConfig { - rootPath: string; - } - - export interface Principal { - details: any; - isAuthenticated(): Promise; - // Allows content-based auth - isResourceOwner(resourceId: any): Promise; - // Allows role-based auth - isInRole(role: string): Promise; - } - - export interface AuthProvider { - getUser( - req: express.Request, - res: express.Response, - next: express.NextFunction - ): Promise; - } - - export interface HttpContext { - request: express.Request; - response: express.Response; - container: inversifyInterfaces.Container; - user: Principal; - } - - export interface IHttpActionResult { - executeAsync(): Promise; - } - - export interface RouteDetails { - route: string; - args?: string[]; - } - - export interface RouteInfo { - controller: any; - endpoints: RouteDetails[]; - } +import * as express from 'express'; +import {interfaces as inversifyInterfaces} from 'inversify'; +import {HTTP_VERBS_ENUM, PARAMETER_TYPE} from './constants'; +import {HttpResponseMessage} from './httpResponseMessage'; + +export type Middleware = (inversifyInterfaces.ServiceIdentifier | express.RequestHandler); + +export interface ControllerMetadata { + path: string; + middleware: Array; + target: any; +} + +export interface ControllerMethodMetadata extends ControllerMetadata { + method: keyof typeof HTTP_VERBS_ENUM; + key: string; +} + +export interface ControllerParameterMetadata { + [methodName: string]: Array; +} + +export interface ParameterMetadata { + parameterName?: string; + injectRoot: boolean; + index: number; + type: PARAMETER_TYPE; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface Controller { } + +export type HandlerDecorator = (target: any, key: string, value: any) => void; + +export type ConfigFunction = (app: express.Application) => void; + +export interface RoutingConfig { + rootPath: string; } -export { interfaces }; +export interface Principal { + details: any; + isAuthenticated(): Promise; + // Allows content-based auth + isResourceOwner(resourceId: any): Promise; + // Allows role-based auth + isInRole(role: string): Promise; +} + +export interface AuthProvider { + getUser( + req: express.Request, + res: express.Response, + next: express.NextFunction + ): Promise; +} + +export interface HttpContext { + request: express.Request; + response: express.Response; + container: inversifyInterfaces.Container; + user: Principal; +} + +export interface IHttpActionResult { + executeAsync(): Promise; +} + +export interface RouteDetails { + route: string; + args?: Array; +} + +export interface RouteInfo { + controller: any; + endpoints: Array; +} + +export interface RawMetadata { + controllerMetadata: ControllerMetadata, + methodMetadata: Array, + parameterMetadata: ControllerParameterMetadata, +} diff --git a/src/results/BadRequestErrorMessageResult.ts b/src/results/BadRequestErrorMessageResult.ts index 13c129c0..50fb9d57 100644 --- a/src/results/BadRequestErrorMessageResult.ts +++ b/src/results/BadRequestErrorMessageResult.ts @@ -1,13 +1,13 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { BAD_REQUEST } from "http-status-codes"; -import { interfaces } from "../interfaces"; -import { StringContent } from "../content/stringContent"; +import {StatusCodes} from 'http-status-codes'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {StringContent} from '../content/stringContent'; +import {IHttpActionResult} from '../interfaces'; -export default class BadRequestErrorMessageResult implements interfaces.IHttpActionResult { +export class BadRequestErrorMessageResult implements IHttpActionResult { constructor(private message: string) { } - public async executeAsync() { - const response = new HttpResponseMessage(BAD_REQUEST); + public async executeAsync(): Promise { + const response = new HttpResponseMessage(StatusCodes.BAD_REQUEST); response.content = new StringContent(this.message); return response; } diff --git a/src/results/BadRequestResult.ts b/src/results/BadRequestResult.ts index 07b816b4..2d2e86ea 100644 --- a/src/results/BadRequestResult.ts +++ b/src/results/BadRequestResult.ts @@ -1,9 +1,9 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { BAD_REQUEST } from "http-status-codes"; -import { interfaces } from "../interfaces"; +import {StatusCodes} from 'http-status-codes'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {IHttpActionResult} from '../interfaces'; -export default class BadRequestResult implements interfaces.IHttpActionResult { - public async executeAsync() { - return new HttpResponseMessage(BAD_REQUEST); +export class BadRequestResult implements IHttpActionResult { + public async executeAsync(): Promise { + return new HttpResponseMessage(StatusCodes.BAD_REQUEST); } } diff --git a/src/results/ConflictResult.ts b/src/results/ConflictResult.ts index 051a44a7..3cd35361 100644 --- a/src/results/ConflictResult.ts +++ b/src/results/ConflictResult.ts @@ -1,9 +1,9 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { CONFLICT } from "http-status-codes"; -import { interfaces } from "../interfaces"; +import {StatusCodes} from 'http-status-codes'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {IHttpActionResult} from '../interfaces'; -export default class ConflictResult implements interfaces.IHttpActionResult { - public async executeAsync() { - return new HttpResponseMessage(CONFLICT); +export class ConflictResult implements IHttpActionResult { + public async executeAsync(): Promise { + return new HttpResponseMessage(StatusCodes.CONFLICT); } } diff --git a/src/results/CreatedNegotiatedContentResult.ts b/src/results/CreatedNegotiatedContentResult.ts index 50a56daa..198f8088 100644 --- a/src/results/CreatedNegotiatedContentResult.ts +++ b/src/results/CreatedNegotiatedContentResult.ts @@ -1,16 +1,16 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { CREATED } from "http-status-codes"; -import { interfaces } from "../interfaces"; -import { URL } from "url"; -import { StringContent } from "../content/stringContent"; +import {StatusCodes} from 'http-status-codes'; +import {URL} from 'url'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {StringContent} from '../content/stringContent'; +import {IHttpActionResult} from '../interfaces'; -export default class CreatedNegotiatedContentResult implements interfaces.IHttpActionResult { +export class CreatedNegotiatedContentResult implements IHttpActionResult { constructor(private location: string | URL, private content: T) { } - public async executeAsync() { - const response = new HttpResponseMessage(CREATED); + public async executeAsync(): Promise { + const response = new HttpResponseMessage(StatusCodes.CREATED); response.content = new StringContent(JSON.stringify(this.content)); - response.headers["location"] = this.location.toString(); + response.headers['location'] = this.location.toString(); return response; } } diff --git a/src/results/ExceptionResult.ts b/src/results/ExceptionResult.ts index e781e693..67da0532 100644 --- a/src/results/ExceptionResult.ts +++ b/src/results/ExceptionResult.ts @@ -1,13 +1,13 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { INTERNAL_SERVER_ERROR } from "http-status-codes"; -import { interfaces } from "../interfaces"; -import { StringContent } from "../content/stringContent"; +import {StatusCodes} from 'http-status-codes'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {StringContent} from '../content/stringContent'; +import {IHttpActionResult} from '../interfaces'; -export default class ExceptionResult implements interfaces.IHttpActionResult { +export class ExceptionResult implements IHttpActionResult { constructor(private error: Error) { } - public async executeAsync() { - const response = new HttpResponseMessage(INTERNAL_SERVER_ERROR); + public async executeAsync(): Promise { + const response = new HttpResponseMessage(StatusCodes.INTERNAL_SERVER_ERROR); response.content = new StringContent(this.error.toString()); return response; } diff --git a/src/results/InternalServerError.ts b/src/results/InternalServerError.ts index ab9f5b5c..975a9c57 100644 --- a/src/results/InternalServerError.ts +++ b/src/results/InternalServerError.ts @@ -1,9 +1,9 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { INTERNAL_SERVER_ERROR } from "http-status-codes"; -import { interfaces } from "../interfaces"; +import {StatusCodes} from 'http-status-codes'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {IHttpActionResult} from '../interfaces'; -export default class InternalServerErrorResult implements interfaces.IHttpActionResult { - public async executeAsync() { - return new HttpResponseMessage(INTERNAL_SERVER_ERROR); +export class InternalServerErrorResult implements IHttpActionResult { + public async executeAsync(): Promise { + return new HttpResponseMessage(StatusCodes.INTERNAL_SERVER_ERROR); } } diff --git a/src/results/JsonResult.ts b/src/results/JsonResult.ts index e3261770..6bef15b6 100644 --- a/src/results/JsonResult.ts +++ b/src/results/JsonResult.ts @@ -1,15 +1,13 @@ -import { interfaces } from "../interfaces"; -import { HttpResponseMessage } from "../httpResponseMessage"; -import { JsonContent } from "../content/jsonContent"; - -export default class JsonResult implements interfaces.IHttpActionResult { - - constructor(public readonly json: any, public readonly statusCode: number) { } - - public async executeAsync() { - const response = new HttpResponseMessage(this.statusCode); - response.content = new JsonContent(this.json); - return response; - } - +import {HttpResponseMessage} from '../httpResponseMessage'; +import {JsonContent} from '../content/jsonContent'; +import {IHttpActionResult} from '../interfaces'; + +export class JsonResult implements IHttpActionResult { + constructor(public readonly json: any, public readonly statusCode: number) { } + + public async executeAsync(): Promise { + const response = new HttpResponseMessage(this.statusCode); + response.content = new JsonContent(this.json); + return response; + } } diff --git a/src/results/NotFoundResult.ts b/src/results/NotFoundResult.ts index 1d0d9823..d4451053 100644 --- a/src/results/NotFoundResult.ts +++ b/src/results/NotFoundResult.ts @@ -1,9 +1,9 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { NOT_FOUND } from "http-status-codes"; -import { interfaces } from "../interfaces"; +import {StatusCodes} from 'http-status-codes'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {IHttpActionResult} from '../interfaces'; -export default class NotFoundResult implements interfaces.IHttpActionResult { - public async executeAsync() { - return new HttpResponseMessage(NOT_FOUND); +export class NotFoundResult implements IHttpActionResult { + public async executeAsync(): Promise { + return new HttpResponseMessage(StatusCodes.NOT_FOUND); } } diff --git a/src/results/OkNegotiatedContentResult.ts b/src/results/OkNegotiatedContentResult.ts index a0530cee..6f01797a 100644 --- a/src/results/OkNegotiatedContentResult.ts +++ b/src/results/OkNegotiatedContentResult.ts @@ -1,13 +1,13 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { OK } from "http-status-codes"; -import { interfaces } from "../interfaces"; -import { StringContent } from "../content/stringContent"; +import {StatusCodes} from 'http-status-codes'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {StringContent} from '../content/stringContent'; +import {IHttpActionResult} from '../interfaces'; -export default class OkNegotiatedContentResult implements interfaces.IHttpActionResult { +export class OkNegotiatedContentResult implements IHttpActionResult { constructor(private content: T) { } - public async executeAsync() { - const response = new HttpResponseMessage(OK); + public async executeAsync(): Promise { + const response = new HttpResponseMessage(StatusCodes.OK); response.content = new StringContent(JSON.stringify(this.content)); return response; } diff --git a/src/results/OkResult.ts b/src/results/OkResult.ts index 1c7505ab..2a3004c3 100644 --- a/src/results/OkResult.ts +++ b/src/results/OkResult.ts @@ -1,10 +1,9 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { OK } from "http-status-codes"; -import { interfaces } from "../interfaces"; +import {StatusCodes} from 'http-status-codes'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {IHttpActionResult} from '../interfaces'; -export default class OkResult implements interfaces.IHttpActionResult { - - public async executeAsync() { - return new HttpResponseMessage(OK); +export class OkResult implements IHttpActionResult { + public async executeAsync(): Promise { + return new HttpResponseMessage(StatusCodes.OK); } } diff --git a/src/results/RedirectResult.ts b/src/results/RedirectResult.ts index 38e74d11..865d300b 100644 --- a/src/results/RedirectResult.ts +++ b/src/results/RedirectResult.ts @@ -1,14 +1,14 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { MOVED_TEMPORARILY } from "http-status-codes"; -import { interfaces } from "../interfaces"; -import { URL } from "url"; +import {StatusCodes} from 'http-status-codes'; +import {URL} from 'url'; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {IHttpActionResult} from '../interfaces'; -export default class RedirectResult implements interfaces.IHttpActionResult { +export class RedirectResult implements IHttpActionResult { constructor(private location: string | URL) { } - public async executeAsync() { - const response = new HttpResponseMessage(MOVED_TEMPORARILY); - response.headers["location"] = this.location.toString(); + public async executeAsync(): Promise { + const response = new HttpResponseMessage(StatusCodes.MOVED_TEMPORARILY); + response.headers['location'] = this.location.toString(); return response; } } diff --git a/src/results/ResponseMessageResult.ts b/src/results/ResponseMessageResult.ts index 22231be3..8a7ee7c6 100644 --- a/src/results/ResponseMessageResult.ts +++ b/src/results/ResponseMessageResult.ts @@ -1,10 +1,10 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { interfaces } from "../interfaces"; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {IHttpActionResult} from '../interfaces'; -export default class ResponseMessageResult implements interfaces.IHttpActionResult { +export class ResponseMessageResult implements IHttpActionResult { constructor(private message: HttpResponseMessage) { } - public async executeAsync() { + public async executeAsync(): Promise { return this.message; } } diff --git a/src/results/StatusCodeResult.ts b/src/results/StatusCodeResult.ts index e7549c64..4fc34d84 100644 --- a/src/results/StatusCodeResult.ts +++ b/src/results/StatusCodeResult.ts @@ -1,10 +1,10 @@ -import { HttpResponseMessage } from "../httpResponseMessage"; -import { interfaces } from "../interfaces"; +import {HttpResponseMessage} from '../httpResponseMessage'; +import {IHttpActionResult} from '../interfaces'; -export default class StatusCodeResult implements interfaces.IHttpActionResult { +export class StatusCodeResult implements IHttpActionResult { constructor(private statusCode: number) { } - public async executeAsync() { + public async executeAsync(): Promise { return new HttpResponseMessage(this.statusCode); } } diff --git a/src/results/index.ts b/src/results/index.ts index 365feb14..f2589d74 100644 --- a/src/results/index.ts +++ b/src/results/index.ts @@ -1,13 +1,13 @@ -export { default as ExceptionResult } from "./ExceptionResult"; -export { default as BadRequestResult } from "./BadRequestResult"; -export { default as BadRequestErrorMessageResult } from "./BadRequestErrorMessageResult"; -export { default as CreatedNegotiatedContentResult } from "./CreatedNegotiatedContentResult"; -export { default as InternalServerErrorResult } from "./InternalServerError"; -export { default as NotFoundResult } from "./NotFoundResult"; -export { default as OkNegotiatedContentResult } from "./OkNegotiatedContentResult"; -export { default as OkResult } from "./OkResult"; -export { default as RedirectResult } from "./RedirectResult"; -export { default as ResponseMessageResult } from "./ResponseMessageResult"; -export { default as ConflictResult } from "./ConflictResult"; -export { default as StatusCodeResult } from "./StatusCodeResult"; -export { default as JsonResult } from "./JsonResult"; +export * from './ExceptionResult'; +export * from './BadRequestResult'; +export * from './BadRequestErrorMessageResult'; +export * from './CreatedNegotiatedContentResult'; +export * from './InternalServerError'; +export * from './NotFoundResult'; +export * from './OkNegotiatedContentResult'; +export * from './OkResult'; +export * from './RedirectResult'; +export * from './ResponseMessageResult'; +export * from './ConflictResult'; +export * from './StatusCodeResult'; +export * from './JsonResult'; diff --git a/src/server.ts b/src/server.ts index 4886dccc..2c6f4b07 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,34 +1,33 @@ -import * as express from "express"; -import * as inversify from "inversify"; -import { interfaces } from "./interfaces"; -import { BaseMiddleware } from "./index"; +import * as express from 'express'; +import * as inversify from 'inversify'; +import {OutgoingHttpHeaders} from 'http'; +import * as interfaces from './interfaces'; +import {BaseMiddleware} from './index'; import { getControllersFromMetadata, getControllersFromContainer, getControllerMetadata, getControllerMethodMetadata, getControllerParameterMetadata, - instanceOfIHttpActionResult -} from "./utils"; + instanceOfIHttpActionResult, +} from './utils'; import { TYPE, METADATA_KEY, DEFAULT_ROUTING_ROOT_PATH, PARAMETER_TYPE, - DUPLICATED_CONTROLLER_NAME -} from "./constants"; -import { HttpResponseMessage } from "./httpResponseMessage"; -import { OutgoingHttpHeaders } from "http"; + DUPLICATED_CONTROLLER_NAME, +} from './constants'; +import {HttpResponseMessage} from './httpResponseMessage'; export class InversifyExpressServer { - private _router: express.Router; private _container: inversify.interfaces.Container; private _app: express.Application; private _configFn!: interfaces.ConfigFunction; private _errorConfigFn!: interfaces.ConfigFunction; private _routingConfig: interfaces.RoutingConfig; - private _AuthProvider!: { new(): interfaces.AuthProvider }; + private _AuthProvider!: new() => interfaces.AuthProvider; private _forceControllers: boolean; /** @@ -46,26 +45,27 @@ export class InversifyExpressServer { customRouter?: express.Router | null, routingConfig?: interfaces.RoutingConfig | null, customApp?: express.Application | null, - authProvider?: { new(): interfaces.AuthProvider } | null, - forceControllers = true + authProvider?: (new() => interfaces.AuthProvider) | null, + forceControllers = true, ) { this._container = container; this._forceControllers = forceControllers; this._router = customRouter || express.Router(); this._routingConfig = routingConfig || { - rootPath: DEFAULT_ROUTING_ROOT_PATH + rootPath: DEFAULT_ROUTING_ROOT_PATH, }; this._app = customApp || express(); if (authProvider) { this._AuthProvider = authProvider; container.bind(TYPE.AuthProvider) - .to(this._AuthProvider); + .to(this._AuthProvider); } } /** * Sets the configuration function to be applied to the application. - * Note that the config function is not actually executed until a call to InversifyExpresServer.build(). + * Note that the config function is not actually executed until a call to + * InversifyExpresServer.build(). * * This method is chainable. * @@ -78,7 +78,8 @@ export class InversifyExpressServer { /** * Sets the error handler configuration function to be applied to the application. - * Note that the error config function is not actually executed until a call to InversifyExpresServer.build(). + * Note that the error config function is not actually executed until a call to + * InversifyExpresServer.build(). * * This method is chainable. * @@ -93,23 +94,20 @@ export class InversifyExpressServer { * Applies all routes and configuration to the server, returning the express application. */ public build(): express.Application { - - const _self = this; - // The very first middleware to be invoked // it creates a new httpContext and attaches it to the // current request as metadata using Reflect - this._app.all("*", ( + this._app.all('*', ( req: express.Request, res: express.Response, - next: express.NextFunction + next: express.NextFunction, ) => { (async () => { - const httpContext = await _self._createHttpContext(req, res, next); + const httpContext = await this._createHttpContext(req, res, next); Reflect.defineMetadata( METADATA_KEY.httpContext, httpContext, - req + req, ); next(); })(); @@ -130,53 +128,55 @@ export class InversifyExpressServer { return this._app; } - private registerControllers() { - + private registerControllers(): void { // Fake HttpContext is needed during registration this._container.bind(TYPE.HttpContext).toConstantValue({} as any); - let constructors = getControllersFromMetadata(); + const constructors = getControllersFromMetadata(); - constructors.forEach((constructor) => { - - const name = constructor.name; + constructors.forEach(constructor => { + const {name} = constructor; if (this._container.isBoundNamed(TYPE.Controller, name)) { throw new Error(DUPLICATED_CONTROLLER_NAME(name)); } this._container.bind(TYPE.Controller) - .to(constructor) - .whenTargetNamed(name); + .to(constructor) + .whenTargetNamed(name); }); - let controllers = getControllersFromContainer( + const controllers = getControllersFromContainer( this._container, - this._forceControllers + this._forceControllers, ); controllers.forEach((controller: interfaces.Controller) => { - - let controllerMetadata = getControllerMetadata(controller.constructor); - let methodMetadata = getControllerMethodMetadata(controller.constructor); - let parameterMetadata = getControllerParameterMetadata(controller.constructor); + const controllerMetadata = getControllerMetadata(controller.constructor); + const methodMetadata = getControllerMethodMetadata(controller.constructor); + const parameterMetadata = getControllerParameterMetadata(controller.constructor); if (controllerMetadata && methodMetadata) { - - let controllerMiddleware = this.resolveMidleware(...controllerMetadata.middleware); + const controllerMiddleware = this.resolveMidleware( + ...controllerMetadata.middleware, + ); methodMetadata.forEach((metadata: interfaces.ControllerMethodMetadata) => { - let paramList: interfaces.ParameterMetadata[] = []; + let paramList: Array = []; if (parameterMetadata) { paramList = parameterMetadata[metadata.key] || []; } - let handler: express.RequestHandler = this.handlerFactory(controllerMetadata.target.name, metadata.key, paramList); - let routeMiddleware = this.resolveMidleware(...metadata.middleware); + const handler: express.RequestHandler = this.handlerFactory( + controllerMetadata.target.name, + metadata.key, + paramList, + ); + const routeMiddleware = this.resolveMidleware(...metadata.middleware); this._router[metadata.method]( - `${controllerMetadata.path}${metadata.path}`, + `${ controllerMetadata.path }${ metadata.path }`, ...controllerMiddleware, ...routeMiddleware, - handler + handler, ); }); } @@ -185,7 +185,9 @@ export class InversifyExpressServer { this._app.use(this._routingConfig.rootPath, this._router); } - private resolveMidleware(...middleware: interfaces.Middleware[]): express.RequestHandler[] { + private resolveMidleware( + ...middleware: Array + ): Array { return middleware.map(middlewareItem => { if (!this._container.isBound(middlewareItem)) { return middlewareItem as express.RequestHandler; @@ -194,14 +196,13 @@ export class InversifyExpressServer { type MiddlewareInstance = express.RequestHandler | BaseMiddleware; const m = this._container.get(middlewareItem); if (m instanceof BaseMiddleware) { - const _self = this; - return function ( + return ( req: express.Request, res: express.Response, - next: express.NextFunction - ) { - let mReq = _self._container.get(middlewareItem); - (mReq as any).httpContext = _self._getHttpContext(req); + next: express.NextFunction, + ): void => { + const mReq = this._container.get(middlewareItem); + (mReq).httpContext = this._getHttpContext(req); mReq.handler(req, res, next); }; } @@ -210,27 +211,30 @@ export class InversifyExpressServer { }); } - private copyHeadersTo(headers: OutgoingHttpHeaders, target: express.Response) { + private copyHeadersTo(headers: OutgoingHttpHeaders, target: express.Response): void { for (const name of Object.keys(headers)) { const headerValue = headers[name]; target.append( name, - typeof headerValue === "number" ? headerValue.toString() : headerValue + typeof headerValue === 'number' ? headerValue.toString() : headerValue, ); } } - private async handleHttpResponseMessage(message: HttpResponseMessage, res: express.Response) { + private async handleHttpResponseMessage( + message: HttpResponseMessage, + res: express.Response, + ): Promise { this.copyHeadersTo(message.headers, res); if (message.content !== undefined) { this.copyHeadersTo(message.content.headers, res); res.status(message.statusCode) - // If the content is a number, ensure we change it to a string, else our content is treated - // as a statusCode rather than as the content of the Response - .send(await message.content.readAsStringAsync()); + // If the content is a number, ensure we change it to a string, else our content is + // treated as a statusCode rather than as the content of the Response + .send(await message.content.readAsStringAsync()); } else { res.sendStatus(message.statusCode); } @@ -239,18 +243,21 @@ export class InversifyExpressServer { private handlerFactory( controllerName: any, key: string, - parameterMetadata: interfaces.ParameterMetadata[] + parameterMetadata: Array, ): express.RequestHandler { return async (req: express.Request, res: express.Response, next: express.NextFunction) => { try { - let args = this.extractParameters(req, res, next, parameterMetadata); + const args = this.extractParameters(req, res, next, parameterMetadata); const httpContext = this._getHttpContext(req); httpContext.container.bind(TYPE.HttpContext) - .toConstantValue(httpContext); + .toConstantValue(httpContext); // invoke controller's action - const value = await httpContext.container.getNamed(TYPE.Controller, controllerName)[key](...args); + const value = await httpContext.container.getNamed( + TYPE.Controller, + controllerName, + )[key](...args); if (value instanceof HttpResponseMessage) { await this.handleHttpResponseMessage(value, res); @@ -274,15 +281,15 @@ export class InversifyExpressServer { private _getHttpContext(req: express.Request): interfaces.HttpContext { return Reflect.getMetadata( METADATA_KEY.httpContext, - req + req, ); } private async _createHttpContext( req: express.Request, res: express.Response, - next: express.NextFunction - ) { + next: express.NextFunction, + ): Promise { const principal = await this._getCurrentUser(req, res, next); return { request: req, @@ -290,35 +297,43 @@ export class InversifyExpressServer { // We use a childContainer for each request so we can be // sure that the binding is unique for each HTTP request container: this._container.createChild(), - user: principal + user: principal, }; } private async _getCurrentUser( req: express.Request, res: express.Response, - next: express.NextFunction + next: express.NextFunction, ): Promise { if (this._AuthProvider !== undefined) { const authProvider = this._container.get(TYPE.AuthProvider); - return await authProvider.getUser(req, res, next); + return authProvider.getUser(req, res, next); } return Promise.resolve({ details: null, isAuthenticated: () => Promise.resolve(false), + // eslint-disable-next-line @typescript-eslint/no-unused-vars isInRole: (role: string) => Promise.resolve(false), - isResourceOwner: (resourceId: any) => Promise.resolve(false) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + isResourceOwner: (resourceId: any) => Promise.resolve(false), }); } - private extractParameters(req: express.Request, res: express.Response, next: express.NextFunction, - params: interfaces.ParameterMetadata[]): any[] { - let args: any[] = []; + private extractParameters( + req: express.Request, + res: express.Response, + next: express.NextFunction, + params: Array, + ): Array { + const args: Array = []; if (!params || !params.length) { return [req, res, next]; } - params.forEach(({ type, index, parameterName, injectRoot }) => { + params.forEach(({ + type, index, parameterName, injectRoot, + }) => { switch (type) { case PARAMETER_TYPE.REQUEST: args[index] = req; @@ -327,19 +342,19 @@ export class InversifyExpressServer { args[index] = next; break; case PARAMETER_TYPE.PARAMS: - args[index] = this.getParam(req, "params", injectRoot, parameterName); + args[index] = this.getParam(req, 'params', injectRoot, parameterName); break; case PARAMETER_TYPE.QUERY: - args[index] = this.getParam(req, "query", injectRoot, parameterName); + args[index] = this.getParam(req, 'query', injectRoot, parameterName); break; case PARAMETER_TYPE.BODY: args[index] = req.body; break; case PARAMETER_TYPE.HEADERS: - args[index] = this.getParam(req, "headers", injectRoot, parameterName); + args[index] = this.getParam(req, 'headers', injectRoot, parameterName); break; case PARAMETER_TYPE.COOKIES: - args[index] = this.getParam(req, "cookies", injectRoot, parameterName); + args[index] = this.getParam(req, 'cookies', injectRoot, parameterName); break; case PARAMETER_TYPE.PRINCIPAL: args[index] = this._getPrincipal(req); @@ -356,18 +371,17 @@ export class InversifyExpressServer { private getParam( source: express.Request, - paramType: "params" | "query" | "headers" | "cookies", + paramType: 'params' | 'query' | 'headers' | 'cookies', injectRoot: boolean, - name?: string - ) { - if (paramType === "headers" && name) { name = name.toLowerCase(); } - let param = source[paramType]; + name?: string, + ): string { + const key = paramType === 'headers' ? name?.toLowerCase() : name; + const param = source[paramType]; if (injectRoot) { return param; - } else { - return (param && name) ? param[name] : undefined; } + return (param && key) ? param[key] : undefined; } private _getPrincipal(req: express.Request): interfaces.Principal | null { diff --git a/src/utils.ts b/src/utils.ts index 38b544e6..ac09046d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,61 +1,70 @@ -import { interfaces as inversifyInterfaces } from "inversify"; -import { METADATA_KEY, NO_CONTROLLERS_FOUND } from "./constants"; -import { interfaces } from "./interfaces"; -import { TYPE } from "./constants"; +import {interfaces} from 'inversify'; +import { + Controller, + ControllerMetadata, + ControllerMethodMetadata, + ControllerParameterMetadata, + IHttpActionResult, +} from './interfaces'; +import {METADATA_KEY, NO_CONTROLLERS_FOUND, TYPE} from './constants'; export function getControllersFromContainer( - container: inversifyInterfaces.Container, - forceControllers: boolean -) { + container: interfaces.Container, + forceControllers: boolean, +): Array { if (container.isBound(TYPE.Controller)) { - return container.getAll(TYPE.Controller); - } else if (forceControllers) { + return container.getAll(TYPE.Controller); + } if (forceControllers) { throw new Error(NO_CONTROLLERS_FOUND); } else { return []; } } -export function getControllersFromMetadata() { - let arrayOfControllerMetadata: interfaces.ControllerMetadata[] = Reflect.getMetadata( +export function getControllersFromMetadata(): Array Controller> { + const arrayOfControllerMetadata: Array = Reflect.getMetadata( METADATA_KEY.controller, - Reflect + Reflect, ) || []; - return arrayOfControllerMetadata.map((metadata) => metadata.target); + return arrayOfControllerMetadata.map(metadata => metadata.target); } -export function getControllerMetadata(constructor: any) { - let controllerMetadata: interfaces.ControllerMetadata = Reflect.getMetadata( +export function getControllerMetadata(constructor: any): ControllerMetadata { + const controllerMetadata: ControllerMetadata = Reflect.getMetadata( METADATA_KEY.controller, - constructor + constructor, ); return controllerMetadata; } -export function getControllerMethodMetadata(constructor: any) { - let methodMetadata: interfaces.ControllerMethodMetadata[] = Reflect.getMetadata( +export function getControllerMethodMetadata( + constructor: any, +): Array { + const methodMetadata: Array = Reflect.getMetadata( METADATA_KEY.controllerMethod, - constructor + constructor, ); return methodMetadata; } -export function getControllerParameterMetadata(constructor: any) { - let parameterMetadata: interfaces.ControllerParameterMetadata = Reflect.getMetadata( +export function getControllerParameterMetadata( + constructor: any, +): ControllerParameterMetadata { + const parameterMetadata: ControllerParameterMetadata = Reflect.getMetadata( METADATA_KEY.controllerParameter, - constructor + constructor, ); return parameterMetadata; } -export function cleanUpMetadata() { +export function cleanUpMetadata(): void { Reflect.defineMetadata( METADATA_KEY.controller, [], - Reflect + Reflect, ); } -export function instanceOfIHttpActionResult(value: any): value is interfaces.IHttpActionResult { - return value != null && typeof value.executeAsync === "function"; +export function instanceOfIHttpActionResult(value: any): value is IHttpActionResult { + return value != null && typeof value.executeAsync === 'function'; } diff --git a/test/.eslintrc.js b/test/.eslintrc.js new file mode 100644 index 00000000..9296f6bc --- /dev/null +++ b/test/.eslintrc.js @@ -0,0 +1,8 @@ +module.exports = { + rules: { + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + 'max-classes-per-file': 'off', + }, +}; diff --git a/test/action_result.test.ts b/test/action_result.test.ts index 725ad684..e69cd62b 100644 --- a/test/action_result.test.ts +++ b/test/action_result.test.ts @@ -1,40 +1,42 @@ -import * as results from "../src/results"; -import { expect } from "chai"; -import * as httpStatusCodes from "http-status-codes"; -import { HttpResponseMessage } from "../src/httpResponseMessage"; +import {expect} from 'chai'; +import * as httpStatusCodes from 'http-status-codes'; +import * as results from '../src/results'; +import {HttpResponseMessage} from '../src/httpResponseMessage'; -describe("ActionResults", function () { - describe("OkResult", function () { - it("should respond with an HTTP 200", async function () { +describe('ActionResults', () => { + describe('OkResult', () => { + it('should respond with an HTTP 200', async () => { const actionResult = new results.OkResult(); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.OK); }); }); - describe("OkNegotiatedContentResult", function () { - it("should respond with an HTTP 200 with content", async function () { + describe('OkNegotiatedContentResult', () => { + it('should respond with an HTTP 200 with content', async () => { const content = { - foo: "bar" + foo: 'bar', }; const actionResult = new results.OkNegotiatedContentResult(content); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.OK); - expect(await responseMessage.content.readAsStringAsync()).to.eq(JSON.stringify(content)); + expect( + await responseMessage.content.readAsStringAsync(), + ).to.eq(JSON.stringify(content)); }); }); - describe("BadRequestResult", function () { - it("should respond with an HTTP 400", async function () { + describe('BadRequestResult', () => { + it('should respond with an HTTP 400', async () => { const actionResult = new results.BadRequestResult(); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.BAD_REQUEST); }); }); - describe("BadRequestErrorMessageResult", function () { - it("should respond with an HTTP 400 and an error message", async function () { - const message = "uh oh!"; + describe('BadRequestErrorMessageResult', () => { + it('should respond with an HTTP 400 and an error message', async () => { + const message = 'uh oh!'; const actionResult = new results.BadRequestErrorMessageResult(message); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.BAD_REQUEST); @@ -42,66 +44,68 @@ describe("ActionResults", function () { }); }); - describe("ConflictResult", function () { - it("should respond with an HTTP 409", async function () { + describe('ConflictResult', () => { + it('should respond with an HTTP 409', async () => { const actionResult = new results.ConflictResult(); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.CONFLICT); }); }); - describe("CreatedNegotiatedContentResult", function () { - it("should respond with an HTTP 302 and appropriate headers", async function () { - const uri = "http://foo/bar"; + describe('CreatedNegotiatedContentResult', () => { + it('should respond with an HTTP 302 and appropriate headers', async () => { + const uri = 'http://foo/bar'; const content = { - foo: "bar" + foo: 'bar', }; const actionResult = new results.CreatedNegotiatedContentResult(uri, content); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.CREATED); - expect(await responseMessage.content.readAsStringAsync()).to.eq(JSON.stringify(content)); - expect(responseMessage.headers["location"]).to.eq(uri); + expect( + await responseMessage.content.readAsStringAsync(), + ).to.eq(JSON.stringify(content)); + expect(responseMessage.headers['location']).to.eq(uri); }); }); - describe("ExceptionResult", function () { - it("should respond with an HTTP 500 and the error message", async function () { - const error = new Error("foo"); + describe('ExceptionResult', () => { + it('should respond with an HTTP 500 and the error message', async () => { + const error = new Error('foo'); const actionResult = new results.ExceptionResult(error); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.INTERNAL_SERVER_ERROR); - expect(await responseMessage.content.readAsStringAsync()).to.eq("Error: foo"); + expect(await responseMessage.content.readAsStringAsync()).to.eq('Error: foo'); }); }); - describe("InternalServerErrorResult", function () { - it("should respond with an HTTP 500", async function () { + describe('InternalServerErrorResult', () => { + it('should respond with an HTTP 500', async () => { const actionResult = new results.InternalServerErrorResult(); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.INTERNAL_SERVER_ERROR); }); }); - describe("NotFoundResult", function () { - it("should respond with an HTTP 404", async function () { + describe('NotFoundResult', () => { + it('should respond with an HTTP 404', async () => { const actionResult = new results.NotFoundResult(); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.NOT_FOUND); }); }); - describe("RedirectResult", function () { - it("should respond with an HTTP 302", async function () { - const uri = "http://foo/bar"; + describe('RedirectResult', () => { + it('should respond with an HTTP 302', async () => { + const uri = 'http://foo/bar'; const actionResult = new results.RedirectResult(uri); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(httpStatusCodes.MOVED_TEMPORARILY); - expect(responseMessage.headers["location"]).to.eq(uri); + expect(responseMessage.headers['location']).to.eq(uri); }); }); - describe("ResponseMessageResult", function () { - it("should respond with an HTTP 302", async function () { + describe('ResponseMessageResult', () => { + it('should respond with an HTTP 302', async () => { const responseMessage = new HttpResponseMessage(200); const actionResult = new results.ResponseMessageResult(responseMessage); @@ -109,8 +113,8 @@ describe("ActionResults", function () { }); }); - describe("StatusCodeResult", function () { - it("should respond with the specified status code", async function () { + describe('StatusCodeResult', () => { + it('should respond with the specified status code', async () => { const actionResult = new results.StatusCodeResult(417); const responseMessage = await actionResult.executeAsync(); expect(responseMessage.statusCode).to.eq(417); diff --git a/test/auth_provider.test.ts b/test/auth_provider.test.ts index 7111db36..db9c0d7c 100644 --- a/test/auth_provider.test.ts +++ b/test/auth_provider.test.ts @@ -1,55 +1,56 @@ -import { expect } from "chai"; -import * as express from "express"; -import { Container, injectable, inject } from "inversify"; -import * as supertest from "supertest"; +import {expect} from 'chai'; +import * as express from 'express'; +import {Container, injectable, inject} from 'inversify'; +import * as supertest from 'supertest'; import { InversifyExpressServer, controller, httpGet, BaseHttpController, - interfaces -} from "../src/index"; -import { cleanUpMetadata } from "../src/utils"; +} from '../src/index'; +import * as interfaces from '../src/interfaces'; +import {cleanUpMetadata} from '../src/utils'; -describe("AuthProvider", () => { - - beforeEach((done) => { +describe('AuthProvider', () => { + beforeEach(done => { cleanUpMetadata(); done(); }); - it("Should be able to access current user via HttpContext", (done) => { - + it('Should be able to access current user via HttpContext', done => { interface SomeDependency { name: string; } class Principal implements interfaces.Principal { public details: any; - public constructor(details: any) { + constructor(details: any) { this.details = details; } + public isAuthenticated() { return Promise.resolve(true); } + public isResourceOwner(resourceId: any) { return Promise.resolve(resourceId === 1111); } + public isInRole(role: string) { - return Promise.resolve(role === "admin"); + return Promise.resolve(role === 'admin'); } } @injectable() class CustomAuthProvider implements interfaces.AuthProvider { - @inject("SomeDependency") private readonly _someDependency!: SomeDependency; + @inject('SomeDependency') private readonly _someDependency!: SomeDependency; public getUser( req: express.Request, res: express.Response, - next: express.NextFunction + next: express.NextFunction, ) { const principal = new Principal({ - email: `${this._someDependency.name}@test.com` + email: `${ this._someDependency.name }@test.com`, }); return Promise.resolve(principal); } @@ -59,42 +60,38 @@ describe("AuthProvider", () => { name: string; } - @controller("/") + @controller('/') class TestController extends BaseHttpController { + @inject('SomeDependency') private readonly _someDependency!: SomeDependency; - @inject("SomeDependency") private readonly _someDependency!: SomeDependency; - - @httpGet("/") + @httpGet('/') public async getTest() { if (this.httpContext.user !== null) { - const email = this.httpContext.user.details.email; - const name = this._someDependency.name; + const {email} = this.httpContext.user.details; + const {name} = this._someDependency; const isAuthenticated = await this.httpContext.user.isAuthenticated(); expect(isAuthenticated).eq(true); - return `${email} & ${name}`; - } else { - return null; + return `${ email } & ${ name }`; } + return null; } } const container = new Container(); - container.bind("SomeDependency") - .toConstantValue({ name: "SomeDependency!" }); + container.bind('SomeDependency') + .toConstantValue({name: 'SomeDependency!'}); const server = new InversifyExpressServer( container, null, null, null, - CustomAuthProvider + CustomAuthProvider, ); supertest(server.build()) - .get("/") - .expect(200, `SomeDependency!@test.com & SomeDependency!`, done); - + .get('/') + .expect(200, 'SomeDependency!@test.com & SomeDependency!', done); }); - }); diff --git a/test/base_http_controller.test.ts b/test/base_http_controller.test.ts index 34327dc3..9a58ac28 100644 --- a/test/base_http_controller.test.ts +++ b/test/base_http_controller.test.ts @@ -1,71 +1,70 @@ -import { expect } from "chai"; -import { Container, inject } from "inversify"; -import * as supertest from "supertest"; +import {expect} from 'chai'; +import {Container, inject} from 'inversify'; +import * as supertest from 'supertest'; import { InversifyExpressServer, controller, httpGet, BaseHttpController, - interfaces -} from "../src/index"; -import { cleanUpMetadata } from "../src/utils"; -import { HttpResponseMessage } from "../src/httpResponseMessage"; -import { StringContent } from "../src/content/stringContent"; - -describe("BaseHttpController", () => { - - beforeEach((done) => { +} from '../src/index'; +import * as interfaces from '../src/interfaces'; +import {cleanUpMetadata} from '../src/utils'; +import {HttpResponseMessage} from '../src/httpResponseMessage'; +import {StringContent} from '../src/content/stringContent'; + +describe('BaseHttpController', () => { + beforeEach(done => { cleanUpMetadata(); done(); }); - it("Should contain httpContext instance", (done) => { - + it('Should contain httpContext instance', done => { interface SomeDependency { name: string; } - @controller("/") + @controller('/') + // eslint-disable-next-line @typescript-eslint/no-unused-vars class TestController extends BaseHttpController { private readonly _someDependency: SomeDependency; - public constructor( - @inject("SomeDependency") someDependency: SomeDependency + constructor( + @inject('SomeDependency') someDependency: SomeDependency, ) { super(); this._someDependency = someDependency; } - @httpGet("/") + + @httpGet('/') public async getTest() { - const headerVal = this.httpContext.request.headers["x-custom"]; - const name = this._someDependency.name; + const headerVal = this.httpContext.request.headers['x-custom']; + const {name} = this._someDependency; const isAuthenticated = await this.httpContext.user.isAuthenticated(); expect(isAuthenticated).eq(false); - return `${headerVal} & ${name}`; + return `${ headerVal } & ${ name }`; } } const container = new Container(); - container.bind("SomeDependency") - .toConstantValue({ name: "SomeDependency!" }); + container.bind('SomeDependency') + .toConstantValue({name: 'SomeDependency!'}); const server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .set("x-custom", "test-header!") - .expect(200, `test-header! & SomeDependency!`, done); - + .get('/') + .set('x-custom', 'test-header!') + .expect(200, 'test-header! & SomeDependency!', done); }); - it("should support returning an HttpResponseMessage from a method", async function () { - @controller("/") + it('should support returning an HttpResponseMessage from a method', async () => { + @controller('/') class TestController extends BaseHttpController { - @httpGet("/") + @httpGet('/') public async getTest() { const response = new HttpResponseMessage(200); - response.headers["x-custom"] = "test-header"; - response.content = new StringContent("12345"); + response.headers['x-custom'] = 'test-header'; + response.content = new StringContent('12345'); return response; } } @@ -73,31 +72,31 @@ describe("BaseHttpController", () => { const server = new InversifyExpressServer(new Container()); await supertest(server.build()) - .get("/") - .expect(200, "12345") - .expect("x-custom", "test-header") - .expect("content-type", "text/plain; charset=utf-8"); + .get('/') + .expect(200, '12345') + .expect('x-custom', 'test-header') + .expect('content-type', 'text/plain; charset=utf-8'); }); - it("should support returning an IHttpActionResult from a method", async function () { - @controller("/") + it('should support returning an IHttpActionResult from a method', async () => { + @controller('/') class TestController extends BaseHttpController { - @httpGet("/") + @httpGet('/') public async getTest() { return new class TestActionResult implements interfaces.IHttpActionResult { public async executeAsync() { const response = new HttpResponseMessage(400); - response.content = new StringContent("You done did that wrong"); + response.content = new StringContent('You done did that wrong'); return response; } - }; + }(); } } const server = new InversifyExpressServer(new Container()); await supertest(server.build()) - .get("/") - .expect(400, "You done did that wrong"); + .get('/') + .expect(400, 'You done did that wrong'); }); }); diff --git a/test/base_middleware.test.ts b/test/base_middleware.test.ts index 7222130f..a077dc7e 100644 --- a/test/base_middleware.test.ts +++ b/test/base_middleware.test.ts @@ -1,47 +1,50 @@ -import { expect } from "chai"; -import * as async from "async"; -import * as express from "express"; -import { Container, injectable, inject, optional } from "inversify"; -import * as supertest from "supertest"; +import {expect} from 'chai'; +import * as async from 'async'; +import * as express from 'express'; +import { + Container, injectable, inject, optional, +} from 'inversify'; +import * as supertest from 'supertest'; import { InversifyExpressServer, controller, httpGet, BaseMiddleware, BaseHttpController, - interfaces -} from "../src/index"; -import { cleanUpMetadata } from "../src/utils"; - -describe("BaseMiddleware", () => { +} from '../src/index'; +import * as interfaces from '../src/interfaces'; +import {cleanUpMetadata} from '../src/utils'; - beforeEach((done) => { +describe('BaseMiddleware', () => { + beforeEach(done => { cleanUpMetadata(); done(); }); - it("Should be able to inject BaseMiddleware implementations", (done) => { - + it('Should be able to inject BaseMiddleware implementations', done => { const TYPES = { - LoggerMiddleware: Symbol.for("LoggerMiddleware"), - SomeDependency: Symbol.for("SomeDependency") + LoggerMiddleware: Symbol.for('LoggerMiddleware'), + SomeDependency: Symbol.for('SomeDependency'), }; let principalInstanceCount = 0; class Principal implements interfaces.Principal { public details: any; - public constructor(details: any) { + constructor(details: any) { this.details = details; } + public isAuthenticated() { return Promise.resolve(true); } + public isResourceOwner(resourceId: any) { return Promise.resolve(resourceId === 1111); } + public isInRole(role: string) { - return Promise.resolve(role === "admin"); + return Promise.resolve(role === 'admin'); } } @@ -50,11 +53,11 @@ describe("BaseMiddleware", () => { public getUser( req: express.Request, res: express.Response, - next: express.NextFunction + next: express.NextFunction, ) { - principalInstanceCount = principalInstanceCount + 1; + principalInstanceCount += 1; const principal = new Principal({ - email: `test@test.com` + email: 'test@test.com', }); return Promise.resolve(principal); } @@ -64,7 +67,7 @@ describe("BaseMiddleware", () => { name: string; } - const logEntries: string[] = []; + const logEntries: Array = []; @injectable() class LoggerMiddleware extends BaseMiddleware { @@ -72,99 +75,96 @@ describe("BaseMiddleware", () => { public handler( req: express.Request, res: express.Response, - next: express.NextFunction + next: express.NextFunction, ) { - const email = this.httpContext.user.details.email; - logEntries.push(`${email} => ${req.url} ${this._someDependency.name}`); + const {email} = this.httpContext.user.details; + logEntries.push(`${ email } => ${ req.url } ${ this._someDependency.name }`); next(); } } @controller( - "/", + '/', (req, res, next) => { - logEntries.push("Hello from controller middleware!"); + logEntries.push('Hello from controller middleware!'); next(); - } + }, ) class TestController extends BaseHttpController { @httpGet( - "/", - TYPES.LoggerMiddleware + '/', + TYPES.LoggerMiddleware, ) public async getTest() { if (this.httpContext.user !== null) { - const email = this.httpContext.user.details.email; + const {email} = this.httpContext.user.details; const isAuthenticated = await this.httpContext.user.isAuthenticated(); - logEntries.push(`${email} => isAuthenticated() => ${isAuthenticated}`); - return `${email}`; - } else { - return null; + logEntries.push(`${ email } => isAuthenticated() => ${ isAuthenticated }`); + return `${ email }`; } + return null; } } const container = new Container(); container.bind(TYPES.SomeDependency) - .toConstantValue({ name: "SomeDependency!" }); + .toConstantValue({name: 'SomeDependency!'}); container.bind(TYPES.LoggerMiddleware) - .to(LoggerMiddleware); + .to(LoggerMiddleware); const server = new InversifyExpressServer( container, null, null, null, - CustomAuthProvider + CustomAuthProvider, ); supertest(server.build()) - .get("/") - .expect(200, `test@test.com`, () => { - expect(principalInstanceCount).eq( - 1, - "Only one instance of HttpContext should be created per HTTP request!" - ); - expect(logEntries.length).eq(3); - expect(logEntries[0]).eq( - "Hello from controller middleware!", - "Expected controller action to be invoked 1st!" - ); - expect(logEntries[1]).eq( - "test@test.com => / SomeDependency!", - "Expected action middleare to be invoked 2nd!" - ); - expect(logEntries[2]).eq( - "test@test.com => isAuthenticated() => true", - "Expected action to be invoked 3rd!" - ); - done(); - }); + .get('/') + .expect(200, 'test@test.com', () => { + expect(principalInstanceCount).eq( + 1, + 'Only one instance of HttpContext should be created per HTTP request!', + ); + expect(logEntries.length).eq(3); + expect(logEntries[0]).eq( + 'Hello from controller middleware!', + 'Expected controller action to be invoked 1st!', + ); + expect(logEntries[1]).eq( + 'test@test.com => / SomeDependency!', + 'Expected action middleare to be invoked 2nd!', + ); + expect(logEntries[2]).eq( + 'test@test.com => isAuthenticated() => true', + 'Expected action to be invoked 3rd!', + ); + done(); + }); }); - it("Should allow the middleware to inject services in a HTTP request scope", (done) => { - - const TRACE_HEADER = "X-Trace-Id"; + it('Should allow the middleware to inject services in a HTTP request scope', done => { + const TRACE_HEADER = 'X-Trace-Id'; const TYPES = { - TraceIdValue: Symbol.for("TraceIdValue"), - TracingMiddleware: Symbol.for("TracingMiddleware"), - Service: Symbol.for("Service"), + TraceIdValue: Symbol.for('TraceIdValue'), + TracingMiddleware: Symbol.for('TracingMiddleware'), + Service: Symbol.for('Service'), }; @injectable() class TracingMiddleware extends BaseMiddleware { - public handler( req: express.Request, res: express.Response, - next: express.NextFunction + next: express.NextFunction, ) { setTimeout(() => { this.bind(TYPES.TraceIdValue) - .toConstantValue(`${req.header(TRACE_HEADER)}`); + .toConstantValue(`${ req.header(TRACE_HEADER) }`); next(); }, someTimeBetween(0, 500)); } @@ -184,16 +184,15 @@ describe("BaseMiddleware", () => { } } - @controller("/") + @controller('/') class TracingTestController extends BaseHttpController { - constructor(@inject(TYPES.Service) private readonly service: Service) { super(); } @httpGet( - "/", - TYPES.TracingMiddleware + '/', + TYPES.TracingMiddleware, ) public getTest() { return this.service.doSomethingThatRequiresTheTraceID(); @@ -211,62 +210,57 @@ describe("BaseMiddleware", () => { const expectedRequests = 100; let handledRequests = 0; - run(expectedRequests, (executionId: number) => { - return supertest(api) - .get("/") - .set(TRACE_HEADER, `trace-id-${executionId}`) - .expect(200, `trace-id-${executionId}`) - .then(res => { - handledRequests++; - }); - }, (err?: Error | null | undefined) => { + run(expectedRequests, (executionId: number) => supertest(api) + .get('/') + .set(TRACE_HEADER, `trace-id-${ executionId }`) + .expect(200, `trace-id-${ executionId }`) + .then(res => { + handledRequests += 1; + }), (err?: Error | null | undefined) => { expect(handledRequests).eq( expectedRequests, - `Only ${handledRequests} out of ${expectedRequests} have been handled correctly` + `Only ${ handledRequests } out of ${ expectedRequests } have been handled correctly`, ); done(err); }); }); - it("Should not allow services injected into a HTTP request scope to be accessible outside the request scope", (done) => { - + it('Should not allow services injected into a HTTP request scope to be accessible outside the request scope', done => { const TYPES = { - Transaction: Symbol.for("Transaction"), - TransactionMiddleware: Symbol.for("TransactionMiddleware"), + Transaction: Symbol.for('Transaction'), + TransactionMiddleware: Symbol.for('TransactionMiddleware'), }; class TransactionMiddleware extends BaseMiddleware { - private count = 0; public handler( req: express.Request, res: express.Response, - next: express.NextFunction + next: express.NextFunction, ) { this.bind(TYPES.Transaction) - .toConstantValue(`I am transaction #${++this.count}\n`); + .toConstantValue(`I am transaction #${ this.count += 1 }\n`); next(); } } - @controller("/") + @controller('/') class TransactionTestController extends BaseHttpController { - constructor(@inject(TYPES.Transaction) @optional() private transaction: string) { super(); } @httpGet( - "/1", - TYPES.TransactionMiddleware + '/1', + TYPES.TransactionMiddleware, ) public getTest1() { return this.transaction; } @httpGet( - "/2" // <= No middleware! + '/2', // <= No middleware! ) public getTest2() { return this.transaction; @@ -275,33 +269,38 @@ describe("BaseMiddleware", () => { const container = new Container(); - container.bind(TYPES.TransactionMiddleware).to(TransactionMiddleware); + container.bind( + TYPES.TransactionMiddleware, + ).to(TransactionMiddleware); const app = new InversifyExpressServer(container).build(); supertest(app) - .get("/1") - .expect(200, "I am transaction #1", () => { - + .get('/1') + .expect(200, 'I am transaction #1', () => { + supertest(app) + .get('/1') + .expect(200, 'I am transaction #2', () => { supertest(app) - .get("/1") - .expect(200, "I am transaction #2", () => { - - supertest(app) - .get("/2") - .expect(200, "", () => done()); - }); + .get('/2') + .expect(200, '', () => done()); }); - + }); }); }); - -function run(parallelRuns: number, test: (executionId: number) => PromiseLike, done: (error?: Error | null | undefined) => void) { - const testTaskNo = (id: number) => function (cb: (err?: Error | null | undefined) => void) { +function run( + parallelRuns: number, + test: (executionId: number) => PromiseLike, + done: (error?: Error | null | undefined) => void, +) { + const testTaskNo = (id: number) => (cb: (err?: Error | null | undefined) => void) => { test(id).then(cb, cb); }; - const testTasks = Array.from({ length: parallelRuns }, (val: undefined, key: number) => testTaskNo(key)); + const testTasks = Array.from( + {length: parallelRuns}, + (val: undefined, key: number) => testTaskNo(key), + ); async.parallel(testTasks, done); } @@ -311,4 +310,3 @@ function someTimeBetween(minimum: number, maximum: number) { const max = Math.floor(maximum); return Math.floor(Math.random() * (max - min + 1)) + min; } - diff --git a/test/bugs.test.ts b/test/bugs.test.ts index 206af0e8..0df4fbb3 100644 --- a/test/bugs.test.ts +++ b/test/bugs.test.ts @@ -1,266 +1,263 @@ -import { expect } from "chai"; -import * as express from "express"; -import { InversifyExpressServer } from "../src/server"; -import { Container } from "inversify"; -import * as supertest from "supertest"; -import * as cookieParser from "cookie-parser"; +import {expect} from 'chai'; +import * as express from 'express'; +import {Container} from 'inversify'; +import * as supertest from 'supertest'; +import * as cookieParser from 'cookie-parser'; +import {InversifyExpressServer} from '../src/server'; import { controller, httpGet, request, - response, requestParam, queryParam, requestHeaders, cookies -} from "../src/decorators"; -import { cleanUpMetadata } from "../src/utils"; + response, requestParam, queryParam, requestHeaders, cookies, +} from '../src/decorators'; +import {cleanUpMetadata} from '../src/utils'; -describe("Unit Test: Previous bugs", () => { - - beforeEach((done) => { +describe('Unit Test: Previous bugs', () => { + beforeEach(done => { cleanUpMetadata(); done(); }); - it("should support multiple controller methods with param annotations", (done) => { - - let container = new Container(); + it('should support multiple controller methods with param annotations', done => { + const container = new Container(); - @controller("/api/test") + @controller('/api/test') class TestController { - @httpGet("/") + @httpGet('/') public get( - @request() req: express.Request, - @response() res: express.Response + @request() req: express.Request, + @response() res: express.Response, ) { expect(req.url).not.to.eql(undefined); expect((req as any).setHeader).to.eql(undefined); + // eslint-disable-next-line @typescript-eslint/unbound-method expect(res.setHeader).not.to.eql(undefined); expect((res as any).url).to.eql(undefined); - res.json([{ id: 1 }, { id: 2 }]); + res.json([{id: 1}, {id: 2}]); } - @httpGet("/:id") + + @httpGet('/:id') public getById( - @requestParam("id") id: string, + @requestParam('id') id: string, @request() req: express.Request, - @response() res: express.Response + @response() res: express.Response, ) { - expect(id).to.eql("5"); + expect(id).to.eql('5'); expect(req.url).not.to.eql(undefined); expect((req as any).setHeader).to.eql(undefined); + // eslint-disable-next-line @typescript-eslint/unbound-method expect(res.setHeader).not.to.eql(undefined); expect((res as any).url).to.eql(undefined); - res.json({ id: id }); + res.json({id}); } } - let server = new InversifyExpressServer(container); - let app = server.build(); - - supertest(app).get("/api/test/") - .expect("Content-Type", /json/) - .expect(200) - .then(response1 => { - expect(Array.isArray(response1.body)).to.eql(true); - expect(response1.body[0].id).to.eql(1); - expect(response1.body[1].id).to.eql(2); - }).catch(done); - - supertest(app).get("/api/test/5") - .expect("Content-Type", /json/) - .expect(200) - .then(response2 => { - expect(Array.isArray(response2.body)).to.eql(false); - expect(response2.body.id).to.eql("5"); - done(); - }).catch(done); - + const server = new InversifyExpressServer(container); + const app = server.build(); + + supertest(app).get('/api/test/') + .expect('Content-Type', /json/) + .expect(200) + .then(response1 => { + expect(Array.isArray(response1.body)).to.eql(true); + expect(response1.body[0].id).to.eql(1); + expect(response1.body[1].id).to.eql(2); + }) + .catch(done); + + supertest(app).get('/api/test/5') + .expect('Content-Type', /json/) + .expect(200) + .then(response2 => { + expect(Array.isArray(response2.body)).to.eql(false); + expect(response2.body.id).to.eql('5'); + done(); + }) + .catch(done); }); - it("should support empty query params", (done) => { - let container = new Container(); + it('should support empty query params', done => { + const container = new Container(); - @controller("/api/test") + @controller('/api/test') class TestController { - @httpGet("/") + @httpGet('/') public get( - @request() req: express.Request, + @request() req: express.Request, @response() res: express.Response, - @queryParam("empty") empty: string, - @queryParam("test") test: string + @queryParam('empty') empty: string, + @queryParam('test') test: string, ) { - return { empty: empty, test: test }; + return {empty, test}; } - } - let server = new InversifyExpressServer(container); - let app = server.build(); - - supertest(app).get("/api/test?test=testquery") - .expect("Content-Type", /json/) - .expect(200) - .then(response1 => { - expect(response1.body.test).to.eql("testquery"); - expect(response1.body.empty).to.eq(undefined); - done(); - }).catch(done); - + const server = new InversifyExpressServer(container); + const app = server.build(); + + supertest(app).get('/api/test?test=testquery') + .expect('Content-Type', /json/) + .expect(200) + .then(response1 => { + expect(response1.body.test).to.eql('testquery'); + expect(response1.body.empty).to.eq(undefined); + done(); + }) + .catch(done); }); - it("should support empty headers params", (done) => { - let container = new Container(); + it('should support empty headers params', done => { + const container = new Container(); - @controller("/api/test") + @controller('/api/test') class TestController { - @httpGet("/") + @httpGet('/') public get( - @request() req: express.Request, + @request() req: express.Request, @response() res: express.Response, - @requestHeaders("TestHead") test: string, - @requestHeaders("empty") empty: string + @requestHeaders('TestHead') test: string, + @requestHeaders('empty') empty: string, ) { - return { empty: empty, test: test }; + return {empty, test}; } - } - let server = new InversifyExpressServer(container); - let app = server.build(); - - supertest(app).get("/api/test?test=testquery") - .expect("Content-Type", /json/) - .set("TestHead", "foo") - .expect(200) - .then(response1 => { - expect(response1.body.test).to.eql("foo"); - expect(response1.body.empty).to.eq(undefined); - done(); - }).catch(done); - + const server = new InversifyExpressServer(container); + const app = server.build(); + + supertest(app).get('/api/test?test=testquery') + .expect('Content-Type', /json/) + .set('TestHead', 'foo') + .expect(200) + .then(response1 => { + expect(response1.body.test).to.eql('foo'); + expect(response1.body.empty).to.eq(undefined); + done(); + }) + .catch(done); }); - describe("param objects without a name (key)", () => { - it("should be injected for params", (done) => { - let container = new Container(); + describe('param objects without a name (key)', () => { + it('should be injected for params', done => { + const container = new Container(); - @controller("/api/test") + @controller('/api/test') class TestController { - @httpGet("/:id/:other") + @httpGet('/:id/:other') public get( - @request() req: express.Request, + @request() req: express.Request, @response() res: express.Response, - @requestParam() params: object + @requestParam() params: any, ) { - return { ...params }; + return {...params}; } - } - let server = new InversifyExpressServer(container); - let app = server.build(); - - supertest(app).get("/api/test/23/andMe") - .expect("Content-Type", /json/) - .expect(200) - .then(res => { - expect(res.body.id).to.eql("23"); - expect(res.body.other).to.eq("andMe"); - done(); - }).catch(done); + const server = new InversifyExpressServer(container); + const app = server.build(); + + supertest(app).get('/api/test/23/andMe') + .expect('Content-Type', /json/) + .expect(200) + .then(res => { + expect(res.body.id).to.eql('23'); + expect(res.body.other).to.eq('andMe'); + done(); + }) + .catch(done); }); - it("should be injected for query params", (done) => { - let container = new Container(); + it('should be injected for query params', done => { + const container = new Container(); - @controller("/api/test") + @controller('/api/test') class TestController { - @httpGet("/") + @httpGet('/') public get( - @request() req: express.Request, + @request() req: express.Request, @response() res: express.Response, - @queryParam() query: object + @queryParam() query: any, ) { - return { ...query }; + return {...query}; } - } - let server = new InversifyExpressServer(container); - let app = server.build(); - - supertest(app).get("/api/test?id=23&other=andMe") - .expect("Content-Type", /json/) - .expect(200) - .then(res => { - expect(res.body.id).to.eql("23"); - expect(res.body.other).to.eq("andMe"); - done(); - }).catch(done); + const server = new InversifyExpressServer(container); + const app = server.build(); + + supertest(app).get('/api/test?id=23&other=andMe') + .expect('Content-Type', /json/) + .expect(200) + .then(res => { + expect(res.body.id).to.eql('23'); + expect(res.body.other).to.eq('andMe'); + done(); + }) + .catch(done); }); - it("should be injected for cookie params", (done) => { - let container = new Container(); + it('should be injected for cookie params', done => { + const container = new Container(); - @controller("/api/test") + @controller('/api/test') class TestController { - @httpGet("/") + @httpGet('/') public get( - @request() req: express.Request, + @request() req: express.Request, @response() res: express.Response, - @cookies() cookie: object, + @cookies() cookie: any, ) { - return { ...cookie }; + return {...cookie}; } - } - let server = new InversifyExpressServer(container); - server.setConfig((_app) => { + const server = new InversifyExpressServer(container); + server.setConfig(_app => { _app.use(cookieParser()); }); - let app = server.build(); - - supertest(app).get("/api/test") - .set("Cookie", "id=23;other=andMe") - .expect("Content-Type", /json/) - .expect(200) - .then(res => { - console.log(res.body); - expect(res.body.id).to.eql("23"); - expect(res.body.other).to.eq("andMe"); - done(); - }).catch(done); + const app = server.build(); + + supertest(app).get('/api/test') + .set('Cookie', 'id=23;other=andMe') + .expect('Content-Type', /json/) + .expect(200) + .then(res => { + expect(res.body.id).to.eql('23'); + expect(res.body.other).to.eq('andMe'); + done(); + }) + .catch(done); }); - it("should be injected for header params", (done) => { - let container = new Container(); + it('should be injected for header params', done => { + const container = new Container(); - @controller("/api/test") + @controller('/api/test') class TestController { - @httpGet("/") + @httpGet('/') public get( - @request() req: express.Request, + @request() req: express.Request, @response() res: express.Response, - @requestHeaders() headers: object, + @requestHeaders() headers: any, ) { - return { ...headers }; + return {...headers}; } - } - let server = new InversifyExpressServer(container); - - let app = server.build(); - - supertest(app).get("/api/test") - .set("id", "23") - .set("other", "andMe") - .expect("Content-Type", /json/) - .expect(200) - .then(res => { - console.log(res.body); - expect(res.body.id).to.eql("23"); - expect(res.body.other).to.eq("andMe"); - done(); - }).catch(done); + const server = new InversifyExpressServer(container); + + const app = server.build(); + + supertest(app).get('/api/test') + .set('id', '23') + .set('other', 'andMe') + .expect('Content-Type', /json/) + .expect(200) + .then(res => { + expect(res.body.id).to.eql('23'); + expect(res.body.other).to.eq('andMe'); + done(); + }) + .catch(done); }); }); - }); diff --git a/test/content/jsonContent.test.ts b/test/content/jsonContent.test.ts index f98d9a9c..d0281991 100644 --- a/test/content/jsonContent.test.ts +++ b/test/content/jsonContent.test.ts @@ -1,24 +1,24 @@ -import { expect } from "chai"; -import { JsonContent } from "../../src/content/jsonContent"; +import {expect} from 'chai'; +import {JsonContent} from '../../src/content/jsonContent'; -describe("JsonContent", () => { - it("should have application/json as the default media type", () => { - const content = new JsonContent({}); - expect(content.headers["content-type"]).to.equal("application/json"); - }); +describe('JsonContent', () => { + it('should have application/json as the default media type', () => { + const content = new JsonContent({}); + expect(content.headers['content-type']).to.equal('application/json'); + }); - it("should respond with the stringified version of the object", (done) => { - const mockObject = { - type: "fake", - success: true, - count: 6 - }; + it('should respond with the stringified version of the object', done => { + const mockObject = { + type: 'fake', + success: true, + count: 6, + }; - const content = new JsonContent(mockObject); + const content = new JsonContent(mockObject); - content.readAsStringAsync().then((value) => { - expect(value).to.equal(JSON.stringify(mockObject)); - done(); + content.readAsStringAsync().then(value => { + expect(value).to.equal(JSON.stringify(mockObject)); + done(); + }); }); - }); }); diff --git a/test/debug.test.ts b/test/debug.test.ts index f4243a11..466fc51b 100644 --- a/test/debug.test.ts +++ b/test/debug.test.ts @@ -1,7 +1,6 @@ -import { expect } from "chai"; -import { Container } from "inversify"; -import * as prettyjson from "prettyjson"; -import { cleanUpMetadata } from "../src/utils"; +import {expect} from 'chai'; +import {Container} from 'inversify'; +import {cleanUpMetadata} from '../src/utils'; import { InversifyExpressServer, controller, @@ -10,133 +9,122 @@ import { httpPost, httpDelete, getRouteInfo, - BaseHttpController -} from "../src/index"; + BaseHttpController, +} from '../src/index'; -describe("Debug utils", () => { - - beforeEach((done) => { +describe('Debug utils', () => { + beforeEach(done => { cleanUpMetadata(); done(); }); - it("should be able to get router info", () => { - - let container = new Container(); + it('should be able to get router info', () => { + const container = new Container(); - @controller("/api/user") + @controller('/api/user') class UserController extends BaseHttpController { - @httpGet("/") + @httpGet('/') public get() { return {}; } - @httpPost("/") + + @httpPost('/') public post() { return {}; } - @httpDelete("/:id") - public delete(@requestParam("id") id: string) { + + @httpDelete('/:id') + public delete(@requestParam('id') id: string) { return {}; } } - @controller("/api/order") + @controller('/api/order') class OrderController extends BaseHttpController { - @httpGet("/") + @httpGet('/') public get() { return {}; } - @httpPost("/") + + @httpPost('/') public post() { return {}; } - @httpDelete("/:id") - public delete(@requestParam("id") id: string) { + + @httpDelete('/:id') + public delete(@requestParam('id') id: string) { return {}; } } const TYPES = { OrderController: OrderController.name, - UserController: UserController.name + UserController: UserController.name, }; - let server = new InversifyExpressServer(container); + const server = new InversifyExpressServer(container); server.build(); const routeInfo = getRouteInfo(container); - console.log( - prettyjson.render({ CONTROLLERS: routeInfo }) - ); - expect(routeInfo[0]?.controller).to.eq(TYPES.OrderController); - expect(routeInfo[0]?.endpoints[0]?.route).to.eq("GET /api/order/"); + expect(routeInfo[0]?.endpoints[0]?.route).to.eq('GET /api/order/'); expect(routeInfo[0]?.endpoints[0]?.args).to.eq(undefined); - expect(routeInfo[0]?.endpoints[1]?.route).to.eq("POST /api/order/"); + expect(routeInfo[0]?.endpoints[1]?.route).to.eq('POST /api/order/'); expect(routeInfo[0]?.endpoints[1]?.args).to.eq(undefined); - expect(routeInfo[0]?.endpoints[2]?.route).to.eq("DELETE /api/order/:id"); + expect(routeInfo[0]?.endpoints[2]?.route).to.eq('DELETE /api/order/:id'); const arg1 = routeInfo[0]?.endpoints[2]?.args; if (arg1 !== undefined) { - expect(arg1[0]).to.eq("@requestParam id"); + expect(arg1[0]).to.eq('@requestParam id'); } else { - expect(true).to.eq(false, "This line should never be executed!"); + expect(true).to.eq(false, 'This line should never be executed!'); } expect(routeInfo[1]?.controller).to.eq(TYPES.UserController); - expect(routeInfo[1]?.endpoints[0]?.route).to.eq("GET /api/user/"); + expect(routeInfo[1]?.endpoints[0]?.route).to.eq('GET /api/user/'); expect(routeInfo[1]?.endpoints[1]?.args).to.eq(undefined); - expect(routeInfo[1]?.endpoints[1]?.route).to.eq("POST /api/user/"); + expect(routeInfo[1]?.endpoints[1]?.route).to.eq('POST /api/user/'); expect(routeInfo[1]?.endpoints[1]?.args).to.eq(undefined); - expect(routeInfo[1]?.endpoints[2]?.route).to.eq("DELETE /api/user/:id"); + expect(routeInfo[1]?.endpoints[2]?.route).to.eq('DELETE /api/user/:id'); const arg2 = routeInfo[1]?.endpoints[2]?.args; if (arg2 !== undefined) { - expect(arg2[0]).to.eq("@requestParam id"); + expect(arg2[0]).to.eq('@requestParam id'); } else { - expect(true).to.eq(false, "This line should never be executed!"); + expect(true).to.eq(false, 'This line should never be executed!'); } - }); - it("should be able to handle missig parameter metadata", () => { + it('should be able to handle missig parameter metadata', () => { + const container = new Container(); - let container = new Container(); - - @controller("/api/order") + @controller('/api/order') class OrderController extends BaseHttpController { - @httpGet("/") + @httpGet('/') public get() { return {}; } - @httpPost("/") + + @httpPost('/') public post() { return {}; } } const TYPES = { - OrderController: OrderController.name + OrderController: OrderController.name, }; - let server = new InversifyExpressServer(container); + const server = new InversifyExpressServer(container); server.build(); const routeInfo = getRouteInfo(container); - console.log( - prettyjson.render({ CONTROLLERS: routeInfo }) - ); - - console.log(routeInfo); - expect(routeInfo[0]?.controller).to.eq(TYPES.OrderController); - expect(routeInfo[0]?.endpoints[0]?.route).to.eq("GET /api/order/"); + expect(routeInfo[0]?.endpoints[0]?.route).to.eq('GET /api/order/'); expect(routeInfo[0]?.endpoints[0]?.args).to.eq(undefined); - expect(routeInfo[0]?.endpoints[1]?.route).to.eq("POST /api/order/"); + expect(routeInfo[0]?.endpoints[1]?.route).to.eq('POST /api/order/'); expect(routeInfo[0]?.endpoints[1]?.args).to.eq(undefined); - }); - }); diff --git a/test/decorators.test.ts b/test/decorators.test.ts index c73029d4..475f8eab 100644 --- a/test/decorators.test.ts +++ b/test/decorators.test.ts @@ -1,20 +1,24 @@ -import { expect } from "chai"; -import { controller, httpMethod, params } from "../src/decorators"; -import { interfaces } from "../src/interfaces"; -import { HTTP_VERBS_ENUM, METADATA_KEY, PARAMETER_TYPE } from "../src/constants"; - -describe("Unit Test: Controller Decorators", () => { - - it("should add controller metadata to a class when decorated with @controller", (done) => { - let middleware = [function () { return; }, "foo", Symbol.for("bar")]; - let path = "foo"; +import {expect} from 'chai'; +import {controller, httpMethod, params} from '../src/decorators'; +import {HTTP_VERBS_ENUM, METADATA_KEY, PARAMETER_TYPE} from '../src/constants'; +import { + ControllerMetadata, + ControllerMethodMetadata, + ControllerParameterMetadata, + ParameterMetadata, +} from '../src'; + +describe('Unit Test: Controller Decorators', () => { + it('should add controller metadata to a class when decorated with @controller', done => { + const middleware = [() => {}, 'foo', Symbol.for('bar')]; + const path = 'foo'; @controller(path, ...middleware) class TestController { } - let controllerMetadata: interfaces.ControllerMetadata = Reflect.getMetadata( + const controllerMetadata: ControllerMetadata = Reflect.getMetadata( METADATA_KEY.controller, - TestController + TestController, ); expect(controllerMetadata.middleware).eql(middleware); @@ -23,67 +27,67 @@ describe("Unit Test: Controller Decorators", () => { done(); }); - - it("should add method metadata to a class when decorated with @httpMethod", (done) => { - let middleware = [function () { return; }, "bar", Symbol.for("baz")]; - let path = "foo"; - let method: keyof typeof HTTP_VERBS_ENUM = "get"; + it('should add method metadata to a class when decorated with @httpMethod', done => { + const middleware = [() => {}, 'bar', Symbol.for('baz')]; + const path = 'foo'; + const method: keyof typeof HTTP_VERBS_ENUM = 'get'; class TestController { @httpMethod(method, path, ...middleware) - public test() { return; } + public test() { } - @httpMethod("foo" as unknown as keyof typeof HTTP_VERBS_ENUM, "bar") - public test2() { return; } + @httpMethod('foo' as unknown as keyof typeof HTTP_VERBS_ENUM, 'bar') + public test2() { } - @httpMethod("bar" as unknown as keyof typeof HTTP_VERBS_ENUM, "foo") - public test3() { return; } + @httpMethod('bar' as unknown as keyof typeof HTTP_VERBS_ENUM, 'foo') + public test3() { } } - let methodMetadata: interfaces.ControllerMethodMetadata[] = Reflect.getMetadata( + const methodMetadata: Array = Reflect.getMetadata( METADATA_KEY.controllerMethod, - TestController + TestController, ); expect(methodMetadata.length).eql(3); - let metadata: interfaces.ControllerMethodMetadata | undefined = methodMetadata[0]; + const metadata: ControllerMethodMetadata | undefined = methodMetadata[0]; expect(metadata?.middleware).eql(middleware); expect(metadata?.path).eql(path); expect(metadata?.target.constructor).eql(TestController); - expect(metadata?.key).eql("test"); + expect(metadata?.key).eql('test'); expect(metadata?.method).eql(method); done(); }); - it("should add parameter metadata to a class when decorated with @params", (done) => { - let middleware = [function () { return; }, "bar", Symbol.for("baz")]; - let path = "foo"; - let method: keyof typeof HTTP_VERBS_ENUM = "get"; - let methodName = "test"; + it('should add parameter metadata to a class when decorated with @params', done => { + const middleware = [() => {}, 'bar', Symbol.for('baz')]; + const path = 'foo'; + const method: keyof typeof HTTP_VERBS_ENUM = 'get'; + const methodName = 'test'; class TestController { @httpMethod(method, path, ...middleware) - public test(@params(PARAMETER_TYPE.PARAMS, "id") id: any, @params(PARAMETER_TYPE.PARAMS, "cat") cat: any) { return; } + public test(@params(PARAMETER_TYPE.PARAMS, 'id') id: any, @params(PARAMETER_TYPE.PARAMS, 'cat') cat: any) { } - @httpMethod("foo" as unknown as keyof typeof HTTP_VERBS_ENUM, "bar") - public test2(@params(PARAMETER_TYPE.PARAMS, "dog") dog: any) { return; } + @httpMethod('foo' as unknown as keyof typeof HTTP_VERBS_ENUM, 'bar') + public test2(@params(PARAMETER_TYPE.PARAMS, 'dog') dog: any) { } - @httpMethod("bar" as unknown as keyof typeof HTTP_VERBS_ENUM, "foo") - public test3() { return; } + @httpMethod('bar' as unknown as keyof typeof HTTP_VERBS_ENUM, 'foo') + public test3() { } } - let methodMetadataList: interfaces.ControllerParameterMetadata = - Reflect.getMetadata(METADATA_KEY.controllerParameter, TestController); - expect(methodMetadataList.hasOwnProperty("test")).to.eqls(true); + const methodMetadataList: ControllerParameterMetadata = Reflect.getMetadata( + METADATA_KEY.controllerParameter, + TestController, + ); + expect(methodMetadataList['test']).to.eqls(true); - let paramaterMetadataList: interfaces.ParameterMetadata[] | undefined = methodMetadataList[methodName]; - expect(paramaterMetadataList?.length).eql(2); + const metadataList: Array | undefined = methodMetadataList[methodName]; + expect(metadataList?.length).eql(2); - let paramaterMetadata: interfaces.ParameterMetadata | undefined = paramaterMetadataList?.[0]; + const paramaterMetadata: ParameterMetadata | undefined = metadataList?.[0]; expect(paramaterMetadata?.index).eql(0); - expect(paramaterMetadata?.parameterName).eql("id"); + expect(paramaterMetadata?.parameterName).eql('id'); done(); }); - }); diff --git a/test/fetures/controller_inheritance.test.ts b/test/fetures/controller_inheritance.test.ts index 0ea22e41..b2fa6a28 100644 --- a/test/fetures/controller_inheritance.test.ts +++ b/test/fetures/controller_inheritance.test.ts @@ -1,192 +1,172 @@ -import { expect } from "chai"; -import * as bodyParser from "body-parser"; -import { InversifyExpressServer } from "../../src/server"; -import { Container, injectable } from "inversify"; -import * as supertest from "supertest"; +import {expect} from 'chai'; +import * as bodyParser from 'body-parser'; +import {Container, injectable} from 'inversify'; +import * as supertest from 'supertest'; +import {InversifyExpressServer} from '../../src/server'; import { controller, httpGet, requestParam, - httpDelete, httpPost, httpPut, requestBody -} from "../../src/decorators"; -import { cleanUpMetadata } from "../../src/utils"; + httpDelete, httpPost, httpPut, requestBody, +} from '../../src/decorators'; +import {cleanUpMetadata} from '../../src/utils'; function getDemoServer() { - interface Movie { name: string; } - let container = new Container(); + const container = new Container(); @injectable() class GenericController { - - @httpGet("/") + @httpGet('/') public get() { - return { status: "BASE GET!" }; + return {status: 'BASE GET!'}; } - @httpGet("/:id") + @httpGet('/:id') public getById( - @requestParam("id") id: string + @requestParam('id') id: string, ) { - return { status: `BASE GET BY ID! ${id}` }; + return {status: `BASE GET BY ID! ${ id }`}; } - @httpPost("/") + @httpPost('/') public post( - @requestBody() body: T, + @requestBody() body: T, ) { return { - status: `BASE POST!`, - args: body + status: 'BASE POST!', + args: body, }; } - @httpPut("/:id") + @httpPut('/:id') public put( - @requestBody() body: T, - @requestParam("id") id: string + @requestBody() body: T, + @requestParam('id') id: string, ) { return { - status: `BASE PUT! ${id}`, - args: body + status: `BASE PUT! ${ id }`, + args: body, }; } - @httpDelete("/:id") + @httpDelete('/:id') public delete( - @requestParam("id") id: string + @requestParam('id') id: string, ) { - return { status: `BASE DELETE! ${id}` }; + return {status: `BASE DELETE! ${ id }`}; } - } - @controller("/api/v1/movies") + @controller('/api/v1/movies') class MoviesController extends GenericController { - - @httpDelete("/:movieId/actors/:actorId") + @httpDelete('/:movieId/actors/:actorId') public deleteActor( - @requestParam("movieId") movieId: string, - @requestParam("actorId") actorId: string + @requestParam('movieId') movieId: string, + @requestParam('actorId') actorId: string, ) { - return { status: `DERIVED DELETE ACTOR! ${movieId} ${actorId}` }; + return {status: `DERIVED DELETE ACTOR! ${ movieId } ${ actorId }`}; } - } - let app = new InversifyExpressServer(container); + const app = new InversifyExpressServer(container); - app.setConfig((a) => { + app.setConfig(a => { a.use(bodyParser.json()); - a.use(bodyParser.urlencoded({ extended: true })); + a.use(bodyParser.urlencoded({extended: true})); }); - let server = app.build(); + const server = app.build(); return server; - } -describe("Derived controller", () => { - - beforeEach((done) => { +describe('Derived controller', () => { + beforeEach(done => { cleanUpMetadata(); done(); }); - it("Can access methods decorated with @httpGet from parent", (done) => { - + it('Can access methods decorated with @httpGet from parent', done => { const server = getDemoServer(); - supertest(server).get("/api/v1/movies") - .expect(200) - .then(res => { - expect(res.body.status).to.eql("BASE GET!"); - done(); - }); - + supertest(server).get('/api/v1/movies') + .expect(200) + .then(res => { + expect(res.body.status).to.eql('BASE GET!'); + done(); + }); }); - it("Can access methods decorated with @httpGet from parent", (done) => { - + it('Can access methods decorated with @httpGet from parent', done => { const server = getDemoServer(); const id = 5; - supertest(server).get(`/api/v1/movies/${id}`) - .expect(200) - .then(res => { - expect(res.body.status).to.eql(`BASE GET BY ID! ${id}`); - done(); - }); - + supertest(server).get(`/api/v1/movies/${ id }`) + .expect(200) + .then(res => { + expect(res.body.status).to.eql(`BASE GET BY ID! ${ id }`); + done(); + }); }); - it("Can access methods decorated with @httpPost from parent", (done) => { - + it('Can access methods decorated with @httpPost from parent', done => { const server = getDemoServer(); - const movie = { name: "The Shining" }; - const status = `BASE POST!`; - - supertest(server).post(`/api/v1/movies`) - .send(movie) - .set("Content-Type", "application/json") - .set("Accept", "application/json") - .expect(200) - .then(res => { - expect(res.body.status).to.eql(status); - expect(res.body.args).to.eql(movie); - done(); - }); - + const movie = {name: 'The Shining'}; + const status = 'BASE POST!'; + + supertest(server).post('/api/v1/movies') + .send(movie) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200) + .then(res => { + expect(res.body.status).to.eql(status); + expect(res.body.args).to.eql(movie); + done(); + }); }); - it("Can access methods decorated with @httpPut from parent", (done) => { - + it('Can access methods decorated with @httpPut from parent', done => { const server = getDemoServer(); const id = 5; - const movie = { name: "The Shining" }; - - supertest(server).put(`/api/v1/movies/${id}`) - .send(movie) - .set("Content-Type", "application/json") - .set("Accept", "application/json") - .expect(200) - .then(res => { - expect(res.body.status).to.eql(`BASE PUT! ${id}`); - expect(res.body.args).to.eql(movie); - done(); - }); - + const movie = {name: 'The Shining'}; + + supertest(server).put(`/api/v1/movies/${ id }`) + .send(movie) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200) + .then(res => { + expect(res.body.status).to.eql(`BASE PUT! ${ id }`); + expect(res.body.args).to.eql(movie); + done(); + }); }); - it("Can access methods decorated with @httpDelete from parent", (done) => { - + it('Can access methods decorated with @httpDelete from parent', done => { const server = getDemoServer(); const id = 5; - supertest(server).delete(`/api/v1/movies/${id}`) - .expect(200) - .then(res => { - expect(res.body.status).to.eql(`BASE DELETE! ${id}`); - done(); - }); - + supertest(server).delete(`/api/v1/movies/${ id }`) + .expect(200) + .then(res => { + expect(res.body.status).to.eql(`BASE DELETE! ${ id }`); + done(); + }); }); - it("Derived controller can have its own methods", (done) => { - + it('Derived controller can have its own methods', done => { const server = getDemoServer(); const movieId = 5; const actorId = 3; - supertest(server).delete(`/api/v1/movies/${movieId}/actors/${actorId}`) - .expect(200) - .then(res => { - expect(res.body.status).to.eql(`DERIVED DELETE ACTOR! ${movieId} ${actorId}`); - done(); - }); - + supertest(server).delete(`/api/v1/movies/${ movieId }/actors/${ actorId }`) + .expect(200) + .then(res => { + expect(res.body.status).to.eql(`DERIVED DELETE ACTOR! ${ movieId } ${ actorId }`); + done(); + }); }); - }); diff --git a/test/framework.test.ts b/test/framework.test.ts index 78704f91..a25f0201 100644 --- a/test/framework.test.ts +++ b/test/framework.test.ts @@ -1,540 +1,521 @@ -import "reflect-metadata"; -import * as sinon from "sinon"; -import * as supertest from "supertest"; -import { expect } from "chai"; -import * as inversify from "inversify"; -import * as express from "express"; -import * as bodyParser from "body-parser"; -import * as cookieParser from "cookie-parser"; -import { injectable, Container } from "inversify"; -import { interfaces, InversifyExpressServer } from "../src"; +import 'reflect-metadata'; +import * as sinon from 'sinon'; +import * as supertest from 'supertest'; +import {expect} from 'chai'; +import * as inversify from 'inversify'; +import * as express from 'express'; +import * as bodyParser from 'body-parser'; +import * as cookieParser from 'cookie-parser'; +import {injectable, Container} from 'inversify'; +import * as Bluebird from 'bluebird'; +import {it, describe, beforeEach} from 'mocha'; +import {InversifyExpressServer} from '../src'; +import * as interfaces from '../src/interfaces'; import { - controller, httpMethod, all, httpGet, httpPost, httpPut, httpPatch, - httpHead, httpDelete, request, response, requestParam, - requestBody, queryParam, requestHeaders, cookies, - next, principal -} from "../src/decorators"; -import * as Bluebird from "bluebird"; -import { cleanUpMetadata } from "../src/utils"; -import { it, describe, beforeEach } from "mocha"; - -describe("Integration Tests:", () => { - + controller, + httpMethod, + all, + httpGet, + httpPost, + httpPut, + httpPatch, + httpHead, + httpDelete, + request, + response, + requestParam, + requestBody, + queryParam, + requestHeaders, + cookies, + next, + principal, +} from '../src/decorators'; +import {cleanUpMetadata} from '../src/utils'; + +describe('Integration Tests:', () => { let server: InversifyExpressServer; let container: inversify.interfaces.Container; - beforeEach((done) => { + beforeEach(done => { cleanUpMetadata(); container = new Container(); done(); }); - describe("Routing & Request Handling:", () => { - - it("should work for async controller methods", (done) => { - - @controller("/") + describe('Routing & Request Handling:', () => { + it('should work for async controller methods', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response) { - return new Promise(((resolve) => { - setTimeout(resolve, 100, "GET"); + @httpGet('/') public getTest(req: express.Request, res: express.Response) { + return new Promise((resolve => { + setTimeout(resolve, 100, 'GET'); })); } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, "GET", done); + .get('/') + .expect(200, 'GET', done); }); - it("should work for async controller methods that fails", (done) => { - - @controller("/") + it('should work for async controller methods that fails', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response) { + @httpGet('/') public getTest(req: express.Request, res: express.Response) { return new Promise(((resolve, reject) => { - setTimeout(reject, 100, "GET"); + setTimeout(reject, 100, 'GET'); })); } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(500, done); + .get('/') + .expect(500, done); }); - it("should work for async controller methods using non-native Bluebird promise", (done) => { - - @controller("/") + it('should work for async controller methods using non-native Bluebird promise', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response) { - return new Bluebird(((resolve) => { - setTimeout(resolve, 100, "GET"); + @httpGet('/') public getTest(req: express.Request, res: express.Response) { + return new Bluebird((resolve => { + setTimeout(resolve, 100, 'GET'); })); } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, "GET", done); + .get('/') + .expect(200, 'GET', done); }); - it("should work for async controller methods, using non-native Bluebird promise, that fails", (done) => { - - @controller("/") + it('should work for async controller methods, using non-native Bluebird promise, that fails', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response) { + @httpGet('/') public getTest(req: express.Request, res: express.Response) { return new Bluebird(((resolve, reject) => { - setTimeout(reject, 100, "GET"); + setTimeout(reject, 100, 'GET'); })); } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(500, done); + .get('/') + .expect(500, done); }); - it("should work for methods which call nextFunc()", (done) => { - - @controller("/") + it('should work for methods which call nextFunc()', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response, nextFunc: express.NextFunction) { + @httpGet('/') public getTest(req: express.Request, res: express.Response, nextFunc: express.NextFunction) { nextFunc(); } - @httpGet("/") public getTest2(req: express.Request, res: express.Response) { - return "GET"; + @httpGet('/') public getTest2(req: express.Request, res: express.Response) { + return 'GET'; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, "GET", done); + .get('/') + .expect(200, 'GET', done); }); - - it("should work for async methods which call nextFunc()", (done) => { - - @controller("/") + it('should work for async methods which call nextFunc()', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response, nextFunc: express.NextFunction) { - return new Promise(((resolve) => { + @httpGet('/') public getTest(req: express.Request, res: express.Response, nextFunc: express.NextFunction) { + return new Promise((resolve => { setTimeout(() => { nextFunc(); resolve(null); - }, 100, "GET"); + }, 100, 'GET'); })); } - @httpGet("/") public getTest2(req: express.Request, res: express.Response) { - return "GET"; + @httpGet('/') public getTest2(req: express.Request, res: express.Response) { + return 'GET'; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, "GET", done); + .get('/') + .expect(200, 'GET', done); }); - - it("should work for async methods called by nextFunc()", (done) => { - - @controller("/") + it('should work for async methods called by nextFunc()', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response, nextFunc: express.NextFunction) { + @httpGet('/') public getTest(req: express.Request, res: express.Response, nextFunc: express.NextFunction) { return nextFunc; } - @httpGet("/") public getTest2(req: express.Request, res: express.Response) { - return new Promise(((resolve) => { - setTimeout(resolve, 100, "GET"); + @httpGet('/') public getTest2(req: express.Request, res: express.Response) { + return new Promise((resolve => { + setTimeout(resolve, 100, 'GET'); })); } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, "GET", done); + .get('/') + .expect(200, 'GET', done); }); - - it("should work for each shortcut decorator", (done) => { - - @controller("/") + it('should work for each shortcut decorator', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response) { res.send("GET"); } - @httpPost("/") public postTest(req: express.Request, res: express.Response) { res.send("POST"); } - @httpPut("/") public putTest(req: express.Request, res: express.Response) { res.send("PUT"); } - @httpPatch("/") public patchTest(req: express.Request, res: express.Response) { res.send("PATCH"); } - @httpHead("/") public headTest(req: express.Request, res: express.Response) { res.send("HEAD"); } - @httpDelete("/") public deleteTest(req: express.Request, res: express.Response) { res.send("DELETE"); } + @httpGet('/') public getTest(req: express.Request, res: express.Response) { res.send('GET'); } + @httpPost('/') public postTest(req: express.Request, res: express.Response) { res.send('POST'); } + @httpPut('/') public putTest(req: express.Request, res: express.Response) { res.send('PUT'); } + @httpPatch('/') public patchTest(req: express.Request, res: express.Response) { res.send('PATCH'); } + @httpHead('/') public headTest(req: express.Request, res: express.Response) { res.send('HEAD'); } + @httpDelete('/') public deleteTest(req: express.Request, res: express.Response) { res.send('DELETE'); } } server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - let deleteFn = () => { agent.delete("/").expect(200, "DELETE", done); }; - let head = () => { agent.head("/").expect(200, "HEAD", deleteFn); }; - let patch = () => { agent.patch("/").expect(200, "PATCH", head); }; - let put = () => { agent.put("/").expect(200, "PUT", patch); }; - let post = () => { agent.post("/").expect(200, "POST", put); }; - let get = () => { agent.get("/").expect(200, "GET", post); }; + const deleteFn = () => { agent.delete('/').expect(200, 'DELETE', done); }; + const head = () => { agent.head('/').expect(200, 'HEAD', deleteFn); }; + const patch = () => { agent.patch('/').expect(200, 'PATCH', head); }; + const put = () => { agent.put('/').expect(200, 'PUT', patch); }; + const post = () => { agent.post('/').expect(200, 'POST', put); }; + const get = () => { agent.get('/').expect(200, 'GET', post); }; get(); }); - - it("should work for more obscure HTTP methods using the httpMethod decorator", (done) => { - - @controller("/") + it('should work for more obscure HTTP methods using the httpMethod decorator', done => { + @controller('/') class TestController { - @httpMethod("propfind", "/") public getTest(req: express.Request, res: express.Response) { res.send("PROPFIND"); } + @httpMethod('propfind', '/') public getTest(req: express.Request, res: express.Response) { res.send('PROPFIND'); } } server = new InversifyExpressServer(container); supertest(server.build()) - .propfind("/") - .expect(200, "PROPFIND", done); + .propfind('/') + .expect(200, 'PROPFIND', done); }); + it('should use returned values as response', done => { + const result = {hello: 'world'}; - it("should use returned values as response", (done) => { - let result = { "hello": "world" }; - - @controller("/") + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response) { return result; } + @httpGet('/') public getTest(req: express.Request, res: express.Response) { return result; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, JSON.stringify(result), done); + .get('/') + .expect(200, JSON.stringify(result), done); }); - it("should use custom router passed from configuration", () => { - @controller("/CaseSensitive") + it('should use custom router passed from configuration', () => { + @controller('/CaseSensitive') class TestController { - @httpGet("/Endpoint") public get() { - return "Such Text"; + @httpGet('/Endpoint') public get() { + return 'Such Text'; } } const customRouter = express.Router({ - caseSensitive: true + caseSensitive: true, }); server = new InversifyExpressServer(container, customRouter); const app = server.build(); const expectedSuccess = supertest(app) - .get("/CaseSensitive/Endpoint") - .expect(200, "Such Text"); + .get('/CaseSensitive/Endpoint') + .expect(200, 'Such Text'); const expectedNotFound1 = supertest(app) - .get("/casesensitive/endpoint") - .expect(404); + .get('/casesensitive/endpoint') + .expect(404); const expectedNotFound2 = supertest(app) - .get("/CaseSensitive/endpoint") - .expect(404); + .get('/CaseSensitive/endpoint') + .expect(404); return Promise.all([ expectedSuccess, expectedNotFound1, - expectedNotFound2 + expectedNotFound2, ]); - }); - it("should use custom routing configuration", () => { - - @controller("/ping") + it('should use custom routing configuration', () => { + @controller('/ping') class TestController { - @httpGet("/endpoint") public get() { - return "pong"; + @httpGet('/endpoint') public get() { + return 'pong'; } } - server = new InversifyExpressServer(container, null, { rootPath: "/api/v1" }); + server = new InversifyExpressServer(container, null, {rootPath: '/api/v1'}); return supertest(server.build()) - .get("/api/v1/ping/endpoint") - .expect(200, "pong"); + .get('/api/v1/ping/endpoint') + .expect(200, 'pong'); }); - it("should work for controller methods who's return value is falsey", (done) => { - @controller("/user") + it("should work for controller methods who's return value is falsey", done => { + @controller('/user') class TestController { - @httpDelete("/") public async delete(): Promise { - return; - } + // eslint-disable-next-line @typescript-eslint/no-empty-function + @httpDelete('/') public async delete(): Promise {} } server = new InversifyExpressServer(container); supertest(server.build()) - .delete("/user") - .expect(204, "", done); + .delete('/user') + .expect(204, '', done); }); }); - - describe("Middleware:", () => { + describe('Middleware:', () => { let result: string; - let middleware: any = { - a: function (req: express.Request, res: express.Response, nextFunc: express.NextFunction) { - result += "a"; + const middleware: any = { + a(req: express.Request, res: express.Response, nextFunc: express.NextFunction) { + result += 'a'; nextFunc(); }, - b: function (req: express.Request, res: express.Response, nextFunc: express.NextFunction) { - result += "b"; + b(req: express.Request, res: express.Response, nextFunc: express.NextFunction) { + result += 'b'; nextFunc(); }, - c: function (req: express.Request, res: express.Response, nextFunc: express.NextFunction) { - result += "c"; + c(req: express.Request, res: express.Response, nextFunc: express.NextFunction) { + result += 'c'; nextFunc(); - } + }, }; - let spyA = sinon.spy(middleware, "a"); - let spyB = sinon.spy(middleware, "b"); - let spyC = sinon.spy(middleware, "c"); + const spyA = sinon.spy(middleware, 'a'); + const spyB = sinon.spy(middleware, 'b'); + const spyC = sinon.spy(middleware, 'c'); - beforeEach((done) => { - result = ""; + beforeEach(done => { + result = ''; spyA.resetHistory(); spyB.resetHistory(); spyC.resetHistory(); done(); }); - it("should call method-level middleware correctly (GET)", (done) => { - - @controller("/") + it('should call method-level middleware correctly (GET)', done => { + @controller('/') class TestController { - @httpGet("/", spyA, spyB, spyC) public getTest(req: express.Request, res: express.Response) { res.send("GET"); } + @httpGet('/', spyA, spyB, spyC) public getTest(req: express.Request, res: express.Response) { res.send('GET'); } } server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - agent.get("/") - .expect(200, "GET", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + agent.get('/') + .expect(200, 'GET', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - it("should call method-level middleware correctly (POST)", (done) => { - - @controller("/") + it('should call method-level middleware correctly (POST)', done => { + @controller('/') class TestController { - @httpPost("/", spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send("POST"); } + @httpPost('/', spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send('POST'); } } server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - agent.post("/") - .expect(200, "POST", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + agent.post('/') + .expect(200, 'POST', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - it("should call method-level middleware correctly (PUT)", (done) => { - - @controller("/") + it('should call method-level middleware correctly (PUT)', done => { + @controller('/') class TestController { - @httpPut("/", spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send("PUT"); } + @httpPut('/', spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send('PUT'); } } server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - agent.put("/") - .expect(200, "PUT", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + agent.put('/') + .expect(200, 'PUT', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - it("should call method-level middleware correctly (PATCH)", (done) => { - - @controller("/") + it('should call method-level middleware correctly (PATCH)', done => { + @controller('/') class TestController { - @httpPatch("/", spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send("PATCH"); } + @httpPatch('/', spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send('PATCH'); } } server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - agent.patch("/") - .expect(200, "PATCH", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + agent.patch('/') + .expect(200, 'PATCH', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - it("should call method-level middleware correctly (HEAD)", (done) => { - - @controller("/") + it('should call method-level middleware correctly (HEAD)', done => { + @controller('/') class TestController { - @httpHead("/", spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send("HEAD"); } + @httpHead('/', spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send('HEAD'); } } server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - agent.head("/") - .expect(200, "HEAD", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + agent.head('/') + .expect(200, 'HEAD', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - it("should call method-level middleware correctly (DELETE)", (done) => { - - @controller("/") + it('should call method-level middleware correctly (DELETE)', done => { + @controller('/') class TestController { - @httpDelete("/", spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send("DELETE"); } + @httpDelete('/', spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send('DELETE'); } } server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - agent.delete("/") - .expect(200, "DELETE", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + agent.delete('/') + .expect(200, 'DELETE', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - it("should call method-level middleware correctly (ALL)", (done) => { - - @controller("/") + it('should call method-level middleware correctly (ALL)', done => { + @controller('/') class TestController { - @all("/", spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send("ALL"); } + @all('/', spyA, spyB, spyC) public postTest(req: express.Request, res: express.Response) { res.send('ALL'); } } server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - agent.get("/") - .expect(200, "ALL", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + agent.get('/') + .expect(200, 'ALL', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - - it("should call controller-level middleware correctly", (done) => { - - @controller("/", spyA, spyB, spyC) + it('should call controller-level middleware correctly', done => { + @controller('/', spyA, spyB, spyC) class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response) { res.send("GET"); } + @httpGet('/') public getTest(req: express.Request, res: express.Response) { res.send('GET'); } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, "GET", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + .get('/') + .expect(200, 'GET', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - - it("should call server-level middleware correctly", (done) => { - - @controller("/") + it('should call server-level middleware correctly', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response) { res.send("GET"); } + @httpGet('/') public getTest(req: express.Request, res: express.Response) { res.send('GET'); } } server = new InversifyExpressServer(container); - server.setConfig((app) => { + server.setConfig(app => { app.use(spyA); app.use(spyB); app.use(spyC); }); supertest(server.build()) - .get("/") - .expect(200, "GET", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + .get('/') + .expect(200, 'GET', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - - it("should call all middleware in correct order", (done) => { - - @controller("/", spyB) + it('should call all middleware in correct order', done => { + @controller('/', spyB) class TestController { - @httpGet("/", spyC) public getTest(req: express.Request, res: express.Response) { res.send("GET"); } + @httpGet('/', spyC) public getTest(req: express.Request, res: express.Response) { res.send('GET'); } } server = new InversifyExpressServer(container); - server.setConfig((app) => { + server.setConfig(app => { app.use(spyA); }); supertest(server.build()) - .get("/") - .expect(200, "GET", function () { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(spyC.calledOnce).to.eqls(true); - expect(result).to.equal("abc"); - done(); - }); + .get('/') + .expect(200, 'GET', () => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(spyC.calledOnce).to.eqls(true); + expect(result).to.equal('abc'); + done(); + }); }); - it("should resolve controller-level middleware", () => { + it('should resolve controller-level middleware', () => { + const symbolId = Symbol.for('spyA'); + const strId = 'spyB'; - const symbolId = Symbol.for("spyA"); - const strId = "spyB"; - - @controller("/", symbolId, strId) + @controller('/', symbolId, strId) class TestController { - @httpGet("/") public getTest(req: express.Request, res: express.Response) { res.send("GET"); } + @httpGet('/') public getTest(req: express.Request, res: express.Response) { res.send('GET'); } } container.bind(symbolId).toConstantValue(spyA); @@ -542,25 +523,25 @@ describe("Integration Tests:", () => { server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - return agent.get("/") - .expect(200, "GET") - .then(() => { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(result).to.equal("ab"); - }); + return agent.get('/') + .expect(200, 'GET') + .then(() => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(result).to.equal('ab'); + }); }); - it("should resolve method-level middleware", () => { - const symbolId = Symbol.for("spyA"); - const strId = "spyB"; + it('should resolve method-level middleware', () => { + const symbolId = Symbol.for('spyA'); + const strId = 'spyB'; - @controller("/") + @controller('/') class TestController { - @httpGet("/", symbolId, strId) - public getTest(req: express.Request, res: express.Response) { res.send("GET"); } + @httpGet('/', symbolId, strId) + public getTest(req: express.Request, res: express.Response) { res.send('GET'); } } container.bind(symbolId).toConstantValue(spyA); @@ -568,26 +549,25 @@ describe("Integration Tests:", () => { server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - return agent.get("/") - .expect(200, "GET") - .then(() => { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(result).to.equal("ab"); - }); + return agent.get('/') + .expect(200, 'GET') + .then(() => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(result).to.equal('ab'); + }); }); - it("should compose controller- and method-level middleware", () => { - - const symbolId = Symbol.for("spyA"); - const strId = "spyB"; + it('should compose controller- and method-level middleware', () => { + const symbolId = Symbol.for('spyA'); + const strId = 'spyB'; - @controller("/", symbolId) + @controller('/', symbolId) class TestController { - @httpGet("/", strId) - public getTest(req: express.Request, res: express.Response) { res.send("GET"); } + @httpGet('/', strId) + public getTest(req: express.Request, res: express.Response) { res.send('GET'); } } container.bind(symbolId).toConstantValue(spyA); @@ -595,190 +575,180 @@ describe("Integration Tests:", () => { server = new InversifyExpressServer(container); - let agent = supertest(server.build()); + const agent = supertest(server.build()); - return agent.get("/") - .expect(200, "GET") - .then(() => { - expect(spyA.calledOnce).to.eqls(true); - expect(spyB.calledOnce).to.eqls(true); - expect(result).to.equal("ab"); - }); + return agent.get('/') + .expect(200, 'GET') + .then(() => { + expect(spyA.calledOnce).to.eqls(true); + expect(spyB.calledOnce).to.eqls(true); + expect(result).to.equal('ab'); + }); }); }); - describe("Parameters:", () => { - it("should bind a method parameter to the url parameter of the web request", (done) => { - - @controller("/") + describe('Parameters:', () => { + it('should bind a method parameter to the url parameter of the web request', done => { + @controller('/') class TestController { // tslint:disable-next-line:max-line-length - @httpGet(":id") public getTest(@requestParam("id") id: string, req: express.Request, res: express.Response) { + @httpGet(':id') public getTest(@requestParam('id') id: string, req: express.Request, res: express.Response) { return id; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/foo") - .expect(200, "foo", done); + .get('/foo') + .expect(200, 'foo', done); }); - it("should bind a method parameter to the request object", (done) => { - - @controller("/") + it('should bind a method parameter to the request object', done => { + @controller('/') class TestController { - @httpGet(":id") public getTest(@request() req: express.Request) { - return req.params["id"]; + @httpGet(':id') public getTest(@request() req: express.Request) { + return req.params['id']; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/GET") - .expect(200, "GET", done); + .get('/GET') + .expect(200, 'GET', done); }); - it("should bind a method parameter to the response object", (done) => { - - @controller("/") + it('should bind a method parameter to the response object', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(@response() res: express.Response) { - return res.send("foo"); + @httpGet('/') public getTest(@response() res: express.Response) { + return res.send('foo'); } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, "foo", done); + .get('/') + .expect(200, 'foo', done); }); - it("should bind a method parameter to a query parameter", (done) => { - - @controller("/") + it('should bind a method parameter to a query parameter', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(@queryParam("id") id: string) { + @httpGet('/') public getTest(@queryParam('id') id: string) { return id; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .query("id=foo") - .expect(200, "foo", done); + .get('/') + .query('id=foo') + .expect(200, 'foo', done); }); - it("should bind a method parameter to the request body", (done) => { - - @controller("/") + it('should bind a method parameter to the request body', done => { + @controller('/') class TestController { - @httpPost("/") public getTest(@requestBody() reqBody: string) { + @httpPost('/') public getTest(@requestBody() reqBody: string) { return reqBody; } } server = new InversifyExpressServer(container); - let body = { foo: "bar" }; - server.setConfig((app) => { + const body = {foo: 'bar'}; + server.setConfig(app => { app.use(bodyParser.json()); }); supertest(server.build()) - .post("/") - .send(body) - .expect(200, body, done); + .post('/') + .send(body) + .expect(200, body, done); }); - it("should bind a method parameter to the request headers", (done) => { - - @controller("/") + it('should bind a method parameter to the request headers', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(@requestHeaders("testhead") headers: any) { + @httpGet('/') public getTest(@requestHeaders('testhead') headers: any) { return headers; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .set("TestHead", "foo") - .expect(200, "foo", done); + .get('/') + .set('TestHead', 'foo') + .expect(200, 'foo', done); }); - it("should be case insensitive to request headers", (done) => { - - @controller("/") + it('should be case insensitive to request headers', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(@requestHeaders("TestHead") headers: any) { + @httpGet('/') public getTest(@requestHeaders('TestHead') headers: any) { return headers; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .set("TestHead", "foo") - .expect(200, "foo", done); + .get('/') + .set('TestHead', 'foo') + .expect(200, 'foo', done); }); - it("should bind a method parameter to a cookie", (done) => { - - @controller("/") + it('should bind a method parameter to a cookie', done => { + @controller('/') class TestController { - @httpGet("/") public getCookie(@cookies("Cookie") cookie: any, req: express.Request, res: express.Response) { + @httpGet('/') public getCookie(@cookies('Cookie') cookie: any, req: express.Request, res: express.Response) { return cookie; } } server = new InversifyExpressServer(container); - server.setConfig((app) => { + server.setConfig(app => { app.use(cookieParser()); }); supertest(server.build()) - .get("/") - .set("Cookie", "Cookie=hey") - .expect(200, "hey", done); + .get('/') + .set('Cookie', 'Cookie=hey') + .expect(200, 'hey', done); }); - it("should bind a method parameter to the next function", (done) => { - - @controller("/") + it('should bind a method parameter to the next function', done => { + @controller('/') class TestController { - @httpGet("/") public getTest(@next() nextFunc: any) { + @httpGet('/') public getTest(@next() nextFunc: any) { return nextFunc(); } - @httpGet("/") public getResult() { - return "foo"; + + @httpGet('/') public getResult() { + return 'foo'; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, "foo", done); + .get('/') + .expect(200, 'foo', done); }); - it("should bind a method parameter to a principal with null (empty) details when no AuthProvider is set.", (done) => { - - @controller("/") + it('should bind a method parameter to a principal with null (empty) details when no AuthProvider is set.', done => { + @controller('/') class TestController { - @httpGet("/") public getPrincipalTest(@principal() userPrincipal: interfaces.Principal) { + @httpGet('/') public getPrincipalTest(@principal() userPrincipal: interfaces.Principal) { return userPrincipal.details; } } server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .expect(200, "", done); + .get('/') + .expect(200, '', done); }); - it("should bind a method parameter to a principal with valid details when an AuthProvider is set.", (done) => { - - @controller("/") + it('should bind a method parameter to a principal with valid details when an AuthProvider is set.', done => { + @controller('/') class TestController { - @httpGet("/") public async getPrincipalTest(@principal() userPrincipal: interfaces.Principal) { - return await userPrincipal.details; + @httpGet('/') public getPrincipalTest(@principal() userPrincipal: interfaces.Principal) { + return userPrincipal.details; } } @@ -787,23 +757,21 @@ describe("Integration Tests:", () => { public async getUser( req: express.Request, res: express.Response, - nextFunc: express.NextFunction + nextFunc: express.NextFunction, ): Promise { return Promise.resolve({ - details: "something", + details: 'something', isAuthenticated: () => Promise.resolve(true), isResourceOwner: () => Promise.resolve(true), - isInRole: () => Promise.resolve(true) + isInRole: () => Promise.resolve(true), } as interfaces.Principal); } } server = new InversifyExpressServer(container, null, null, null, CustomAuthProvider); supertest(server.build()) - .get("/") - .expect(200, "something", done); + .get('/') + .expect(200, 'something', done); }); - }); - }); diff --git a/test/http_context.test.ts b/test/http_context.test.ts index 00b74c77..43a41a3b 100644 --- a/test/http_context.test.ts +++ b/test/http_context.test.ts @@ -1,56 +1,51 @@ -import { expect } from "chai"; -import { Container, inject } from "inversify"; -import * as supertest from "supertest"; +import {expect} from 'chai'; +import {Container, inject} from 'inversify'; +import * as supertest from 'supertest'; import { InversifyExpressServer, controller, httpGet, - interfaces, - injectHttpContext -} from "../src/index"; -import { cleanUpMetadata } from "../src/utils"; + injectHttpContext, +} from '../src/index'; +import * as interfaces from '../src/interfaces'; +import {cleanUpMetadata} from '../src/utils'; -describe("HttpContex", () => { - - beforeEach((done) => { +describe('HttpContex', () => { + beforeEach(done => { cleanUpMetadata(); done(); }); - it("Should be able to httpContext manually with the @injectHttpContext decorator", (done) => { - + it('Should be able to httpContext manually with the @injectHttpContext decorator', done => { interface SomeDependency { name: string; } - @controller("/") + @controller('/') class TestController { - @injectHttpContext private readonly _httpContext!: interfaces.HttpContext; - @inject("SomeDependency") private readonly _someDependency!: SomeDependency; + @inject('SomeDependency') private readonly _someDependency!: SomeDependency; - @httpGet("/") + @httpGet('/') public async getTest() { - const headerVal = this._httpContext.request.headers["x-custom"]; - const name = this._someDependency.name; + const headerVal = this._httpContext.request.headers['x-custom']; + const {name} = this._someDependency; const isAuthenticated = await this._httpContext.user.isAuthenticated(); expect(isAuthenticated).eq(false); - return `${headerVal} & ${name}`; + return `${ headerVal } & ${ name }`; } } const container = new Container(); - container.bind("SomeDependency") - .toConstantValue({ name: "SomeDependency!" }); + container.bind('SomeDependency') + .toConstantValue({name: 'SomeDependency!'}); const server = new InversifyExpressServer(container); supertest(server.build()) - .get("/") - .set("x-custom", "test-header!") - .expect(200, `test-header! & SomeDependency!`, done); - + .get('/') + .set('x-custom', 'test-header!') + .expect(200, 'test-header! & SomeDependency!', done); }); - }); diff --git a/test/issue_590.test.ts b/test/issue_590.test.ts index 8ceaa3bf..968f473d 100644 --- a/test/issue_590.test.ts +++ b/test/issue_590.test.ts @@ -1,26 +1,24 @@ -import { expect } from "chai"; -import { Container } from "inversify"; -import { InversifyExpressServer, cleanUpMetadata } from "../src/index"; -import { NO_CONTROLLERS_FOUND } from "../src/constants"; - -describe("Issue 590", () => { +import {expect} from 'chai'; +import {Container} from 'inversify'; +import {InversifyExpressServer, cleanUpMetadata} from '../src/index'; +import {NO_CONTROLLERS_FOUND} from '../src/constants'; +describe('Issue 590', () => { beforeEach(() => { cleanUpMetadata(); }); - it("should throw if no bindings for controllers are declared", () => { - let container = new Container(); - let server = new InversifyExpressServer(container); + it('should throw if no bindings for controllers are declared', () => { + const container = new Container(); + const server = new InversifyExpressServer(container); const throws = () => server.build(); expect(throws).to.throw(NO_CONTROLLERS_FOUND); }); - it("should not throw if forceControllers is false and no bindings for controllers are declared", () => { - let container = new Container(); - let server = new InversifyExpressServer(container, null, null, null, null, false); + it('should not throw if forceControllers is false and no bindings for controllers are declared', () => { + const container = new Container(); + const server = new InversifyExpressServer(container, null, null, null, null, false); const throws = () => server.build(); expect(throws).not.to.throw(); }); - }); diff --git a/test/server.test.ts b/test/server.test.ts index 90f3c558..40d20a8c 100644 --- a/test/server.test.ts +++ b/test/server.test.ts @@ -1,47 +1,43 @@ -import { expect } from "chai"; -import * as sinon from "sinon"; -import * as express from "express"; -import { InversifyExpressServer } from "../src/server"; -import { controller } from "../src/decorators"; -import { Container } from "inversify"; -import { cleanUpMetadata } from "../src/utils"; -import { HttpResponseMessage } from "../src"; -import { Mock, Times } from "moq.ts"; - -describe("Unit Test: InversifyExpressServer", () => { - - beforeEach((done) => { +import {expect} from 'chai'; +import * as sinon from 'sinon'; +import * as express from 'express'; +import {Container} from 'inversify'; +import {Mock, Times} from 'moq.ts'; +import {InversifyExpressServer} from '../src/server'; +import {controller} from '../src/decorators'; +import {cleanUpMetadata} from '../src/utils'; +import {HttpResponseMessage} from '../src'; + +describe('Unit Test: InversifyExpressServer', () => { + beforeEach(done => { cleanUpMetadata(); done(); }); - it("should call the configFn before the errorConfigFn", (done) => { - - let middleware = function ( + it('should call the configFn before the errorConfigFn', done => { + const middleware = ( req: express.Request, res: express.Response, - next: express.NextFunction - ) { - return; - }; + next: express.NextFunction, + ) => {}; - let configFn = sinon.spy((app: express.Application) => { + const configFn = sinon.spy((app: express.Application) => { app.use(middleware); }); - let errorConfigFn = sinon.spy((app: express.Application) => { + const errorConfigFn = sinon.spy((app: express.Application) => { app.use(middleware); }); - let container = new Container(); + const container = new Container(); - @controller("/") + @controller('/') class TestController { } - let server = new InversifyExpressServer(container); + const server = new InversifyExpressServer(container); server.setConfig(configFn) - .setErrorConfig(errorConfigFn); + .setErrorConfig(errorConfigFn); expect(configFn.called).to.eq(false); expect(errorConfigFn.called).to.eq(false); @@ -54,47 +50,43 @@ describe("Unit Test: InversifyExpressServer", () => { done(); }); - it("Should allow to pass a custom Router instance as config", () => { + it('Should allow to pass a custom Router instance as config', () => { + const container = new Container(); - let container = new Container(); - - let customRouter = express.Router({ + const customRouter = express.Router({ caseSensitive: false, mergeParams: false, - strict: false + strict: false, }); - let serverWithDefaultRouter = new InversifyExpressServer(container); - let serverWithCustomRouter = new InversifyExpressServer(container, customRouter); + const serverWithDefaultRouter = new InversifyExpressServer(container); + const serverWithCustomRouter = new InversifyExpressServer(container, customRouter); expect((serverWithDefaultRouter as any)._router === customRouter).to.eq(false); expect((serverWithCustomRouter as any)._router === customRouter).to.eqls(true); - }); - it("Should allow to provide custom routing configuration", () => { + it('Should allow to provide custom routing configuration', () => { + const container = new Container(); - let container = new Container(); - - let routingConfig = { - rootPath: "/such/root/path" + const routingConfig = { + rootPath: '/such/root/path', }; - let serverWithDefaultConfig = new InversifyExpressServer(container); - let serverWithCustomConfig = new InversifyExpressServer(container, null, routingConfig); + const serverWithDefaultConfig = new InversifyExpressServer(container); + const serverWithCustomConfig = new InversifyExpressServer(container, null, routingConfig); expect((serverWithCustomConfig as any)._routingConfig).to.eq(routingConfig); expect((serverWithDefaultConfig as any)._routingConfig).to.not.eql( - (serverWithCustomConfig as any)._routingConfig + (serverWithCustomConfig as any)._routingConfig, ); - }); - it("Should allow to provide a custom express application", () => { - let container = new Container(); - let app = express(); - let serverWithDefaultApp = new InversifyExpressServer(container); - let serverWithCustomApp = new InversifyExpressServer(container, null, null, app); + it('Should allow to provide a custom express application', () => { + const container = new Container(); + const app = express(); + const serverWithDefaultApp = new InversifyExpressServer(container); + const serverWithCustomApp = new InversifyExpressServer(container, null, null, app); expect((serverWithCustomApp as any)._app).to.eq(app); expect((serverWithDefaultApp as any)._app).to.not.eql((serverWithCustomApp as any)._app); }); @@ -102,13 +94,16 @@ describe("Unit Test: InversifyExpressServer", () => { // TODO Fix this somehow ?? // https://www.npmjs.com/package/moq.ts/v/3.0.0?activeTab=readme#mock-behavior // Now Obsolete - xit("Should handle a HttpResponseMessage that has no content", () => { - let container = new Container(); - let server = new InversifyExpressServer(container); - - let httpResponseMessageWithoutContent = new HttpResponseMessage(404); - let mockResponse = new Mock(); - (server as any).handleHttpResponseMessage(httpResponseMessageWithoutContent, mockResponse.object()); + xit('Should handle a HttpResponseMessage that has no content', () => { + const container = new Container(); + const server = new InversifyExpressServer(container); + + const httpResponseMessageWithoutContent = new HttpResponseMessage(404); + const mockResponse = new Mock(); + (server as any).handleHttpResponseMessage( + httpResponseMessageWithoutContent, + mockResponse.object(), + ); mockResponse.verify(instance => instance.sendStatus(404), Times.Once()); }); diff --git a/test/tsconfig.json b/test/tsconfig.json index 8eb52148..3dbd281f 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -10,6 +10,7 @@ "extends": "../tsconfig.json", "compilerOptions": { "rootDir": ".", - "noUnusedLocals": false + "noUnusedLocals": false, + "strict": false, }, }