From f04a1edab96e5c58111866f30cc7dab703539f19 Mon Sep 17 00:00:00 2001 From: Rob Muchall Date: Wed, 1 Apr 2020 17:30:30 +0100 Subject: [PATCH 1/4] Jest migration Refactored tests to use Jest/Axios Removed Koa support Replaced mocha, chai, sinon and nyc with Jest Replaced chakram with axios, qs and form-data Removed source-map-support, request and cross-env as they are no longer used Added express typings to ExpressMiddlewareInterface Updated all dependencies Removed tslint in preparation for migration to eslint --- .mocharc.json | 3 - .nycrc.json | 13 - README.md | 2 +- codecov.yml | 1 - jest.config.js | 24 + package-lock.json | 6583 ++++++++++------- package.json | 89 +- .../blog/middlewares/BlogMiddleware.ts | 7 +- .../post/middlewares/PostMiddleware.ts | 7 +- .../middlewares/QuestionMiddleware.ts | 8 +- .../CompressionMiddleware.ts | 7 +- .../EndTimerMiddleware.ts | 7 +- .../LoggerMiddleware.ts | 7 +- .../StartTimerMiddleware.ts | 7 +- .../AllControllerActionsMiddleware.ts | 7 +- .../CompressionMiddleware.ts | 7 +- src/ActionParameterHandler.ts | 11 +- src/RoutingControllers.ts | 3 +- src/decorator/Ctx.ts | 18 - src/decorator/Session.ts | 6 +- src/decorator/SessionParam.ts | 6 +- src/driver/express/ExpressDriver.ts | 42 +- .../ExpressErrorMiddlewareInterface.ts | 9 +- .../express/ExpressMiddlewareInterface.ts | 7 +- src/driver/koa/KoaDriver.ts | 427 -- src/driver/koa/KoaMiddlewareInterface.ts | 12 - src/index.ts | 29 +- src/metadata/ControllerMetadata.ts | 6 +- src/metadata/MiddlewareMetadata.ts | 14 +- src/{ => util}/container.ts | 0 test/fakes/global-options/FakeService.ts | 9 +- .../fakes/global-options/SessionMiddleware.ts | 40 +- .../post/PostMiddleware.ts | 7 +- .../question/QuestionMiddleware.ts | 7 +- .../koa-middlewares/FileMiddleware.ts | 13 - .../koa-middlewares/SetStateMiddleware.ts | 13 - .../koa-middlewares/VideoMiddleware.ts | 13 - test/functional/action-params.spec.ts | 1814 +++-- test/functional/auth-decorator.spec.ts | 316 +- .../class-transformer-options.spec.ts | 187 + test/functional/class-transformer-options.ts | 183 - .../class-validator-options.spec.ts | 393 +- test/functional/container.spec.ts | 729 +- .../functional/controller-base-routes.spec.ts | 99 +- test/functional/controller-methods.spec.ts | 363 +- test/functional/defaults.spec.ts | 131 +- test/functional/error-subclasses.spec.ts | 37 +- .../express-custom-error-handling.spec.ts | 64 +- .../functional/express-error-handling.spec.ts | 119 +- ...press-global-before-error-handling.spec.ts | 67 +- test/functional/express-middlewares.spec.ts | 111 +- test/functional/global-options.spec.ts | 183 +- test/functional/interceptors.spec.ts | 99 +- .../json-controller-methods.spec.ts | 296 +- test/functional/koa-middlewares.spec.ts | 202 - test/functional/load-from-directory.spec.ts | 224 +- test/functional/middlewares-order.spec.ts | 263 +- .../other-controller-decorators.spec.ts | 243 +- test/functional/redirect-decorator.spec.ts | 73 +- test/functional/render-decorator.spec.ts | 92 +- test/functional/special-result-send.spec.ts | 81 +- test/functional/test-utils.ts | 51 - ...text-file.txt => sample-text-file-one.txt} | 0 test/resources/sample-text-file-three.txt | 1 + test/resources/sample-text-file-two.txt | 1 + test/utilities/axios.ts | 5 + tsconfig.json | 84 +- tslint.json | 53 - 68 files changed, 7099 insertions(+), 6936 deletions(-) delete mode 100644 .mocharc.json delete mode 100644 .nycrc.json delete mode 100644 codecov.yml create mode 100644 jest.config.js delete mode 100644 src/decorator/Ctx.ts delete mode 100644 src/driver/koa/KoaDriver.ts delete mode 100644 src/driver/koa/KoaMiddlewareInterface.ts rename src/{ => util}/container.ts (100%) delete mode 100644 test/fakes/global-options/koa-middlewares/FileMiddleware.ts delete mode 100644 test/fakes/global-options/koa-middlewares/SetStateMiddleware.ts delete mode 100644 test/fakes/global-options/koa-middlewares/VideoMiddleware.ts create mode 100644 test/functional/class-transformer-options.spec.ts delete mode 100644 test/functional/class-transformer-options.ts delete mode 100644 test/functional/koa-middlewares.spec.ts delete mode 100644 test/functional/test-utils.ts rename test/resources/{sample-text-file.txt => sample-text-file-one.txt} (100%) create mode 100644 test/resources/sample-text-file-three.txt create mode 100644 test/resources/sample-text-file-two.txt create mode 100644 test/utilities/axios.ts delete mode 100644 tslint.json diff --git a/.mocharc.json b/.mocharc.json deleted file mode 100644 index df2a057c..00000000 --- a/.mocharc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "require": "ts-node/register" -} diff --git a/.nycrc.json b/.nycrc.json deleted file mode 100644 index 286bb46d..00000000 --- a/.nycrc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "all": true, - "check-coverage": true, - "exclude": [ - "**/*.d.ts", - "src/decorator-options/**", - "src/metadata/args/**", - "src/metadata/types/**" - ], - "extension": [".ts"], - "include": ["src/**"], - "reporter": ["html", "lcov", "text-summary"] -} diff --git a/README.md b/README.md index d7dd18b0..898c8fa2 100644 --- a/README.md +++ b/README.md @@ -907,7 +907,7 @@ Here is example of creating middleware for express.js: export class MyMiddleware implements ExpressMiddlewareInterface { // interface implementation is optional - use(request: any, response: any, next?: (err?: any) => any): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("do something..."); next(); } diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index db247200..00000000 --- a/codecov.yml +++ /dev/null @@ -1 +0,0 @@ -comment: off diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..54ad0dbd --- /dev/null +++ b/jest.config.js @@ -0,0 +1,24 @@ +module.exports = { + modulePaths: ["/node_modules"], + transform: { + "^.+\\.tsx?$": "ts-jest" + }, + testRegex: "(/__test__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", + testEnvironment: "node", + moduleFileExtensions: [ + "ts", + "tsx", + "js", + "jsx", + "json", + "node" + ], + modulePathIgnorePatterns: [ + "/build/" + ], + coverageReporters: [ + // "html", + // "lcov", + "text-summary" + ] +}; diff --git a/package-lock.json b/package-lock.json index 44644d79..e7f98ba4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,63 +5,211 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", + "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.0", + "@babel/parser": "^7.9.0", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz", + "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==", "dev": true, "requires": { - "@babel/types": "^7.6.0", + "@babel/types": "^7.9.0", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-imports": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-transforms": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", + "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-simple-access": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/template": "^7.8.6", + "@babel/types": "^7.9.0", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", + "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/helper-simple-access": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", + "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", + "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", + "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", "dev": true, "requires": { - "@babel/types": "^7.4.4" + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0" } }, "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", "dev": true, "requires": { + "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -85,6 +233,21 @@ "supports-color": "^5.3.0" } }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -103,34 +266,52 @@ } }, "@babel/parser": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", - "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, "@babel/template": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", - "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", + "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", "dev": true, "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -154,122 +335,373 @@ } }, "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", + "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", "dev": true, "requires": { - "esutils": "^2.0.2", + "@babel/helper-validator-identifier": "^7.9.0", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", + "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@jest/console": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.3.tgz", + "integrity": "sha512-k+37B1aSvOt9tKHWbZZSOy1jdgzesB0bj96igCVUG1nAH1W5EoUfgc5EXbBVU08KSLvkVdWopLXaO3xfVGlxtQ==", + "dev": true, + "requires": { + "@jest/source-map": "^25.2.1", + "chalk": "^3.0.0", + "jest-util": "^25.2.3", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.2.4.tgz", + "integrity": "sha512-WcWYShl0Bqfcb32oXtjwbiR78D/djhMdJW+ulp4/bmHgeODcsieqUJfUH+kEv8M7VNV77E6jds5aA+WuGh1nmg==", + "dev": true, + "requires": { + "@jest/console": "^25.2.3", + "@jest/reporters": "^25.2.4", + "@jest/test-result": "^25.2.4", + "@jest/transform": "^25.2.4", + "@jest/types": "^25.2.3", + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.3", + "jest-changed-files": "^25.2.3", + "jest-config": "^25.2.4", + "jest-haste-map": "^25.2.3", + "jest-message-util": "^25.2.4", + "jest-regex-util": "^25.2.1", + "jest-resolve": "^25.2.3", + "jest-resolve-dependencies": "^25.2.4", + "jest-runner": "^25.2.4", + "jest-runtime": "^25.2.4", + "jest-snapshot": "^25.2.4", + "jest-util": "^25.2.3", + "jest-validate": "^25.2.3", + "jest-watcher": "^25.2.4", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "realpath-native": "^2.0.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "@jest/environment": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.2.4.tgz", + "integrity": "sha512-wA4xlhD19/gukkDpJ5HQsTle0pgnzI5qMFEjw267lpTDC8d9N7Ihqr5pI+l0p8Qn1SQhai+glSqxrGdzKy4jxw==", + "dev": true, + "requires": { + "@jest/fake-timers": "^25.2.4", + "@jest/types": "^25.2.3", + "jest-mock": "^25.2.3" + } + }, + "@jest/fake-timers": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.2.4.tgz", + "integrity": "sha512-oC1TJiwfMcBttVN7Wz+VZnqEAgYTiEMu0QLOXpypR89nab0uCB31zm/QeBZddhSstn20qe3yqOXygp6OwvKT/Q==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3", + "jest-message-util": "^25.2.4", + "jest-mock": "^25.2.3", + "jest-util": "^25.2.3", + "lolex": "^5.0.0" + } + }, + "@jest/reporters": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.2.4.tgz", + "integrity": "sha512-VHbLxM03jCc+bTLOluW/IqHR2G0Cl0iATwIQbuZtIUast8IXO4fD0oy4jpVGpG5b20S6REA8U3BaQoCW/CeVNQ==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^25.2.3", + "@jest/test-result": "^25.2.4", + "@jest/transform": "^25.2.4", + "@jest/types": "^25.2.3", + "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", + "jest-haste-map": "^25.2.3", + "jest-resolve": "^25.2.3", + "jest-util": "^25.2.3", + "jest-worker": "^25.2.1", + "node-notifier": "^6.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^3.1.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^4.0.1" + } + }, + "@jest/source-map": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.4.tgz", + "integrity": "sha512-AI7eUy+q2lVhFnaibDFg68NGkrxVWZdD6KBr9Hm6EvN0oAe7GxpEwEavgPfNHQjU2mi6g+NsFn/6QPgTUwM1qg==", + "dev": true, + "requires": { + "@jest/console": "^25.2.3", + "@jest/transform": "^25.2.4", + "@jest/types": "^25.2.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.2.4.tgz", + "integrity": "sha512-TEZm/Rkd6YgskdpTJdYLBtu6Gc11tfWPuSpatq0duH77ekjU8dpqX2zkPdY/ayuHxztV5LTJoV5BLtI9mZfXew==", + "dev": true, + "requires": { + "@jest/test-result": "^25.2.4", + "jest-haste-map": "^25.2.3", + "jest-runner": "^25.2.4", + "jest-runtime": "^25.2.4" + } + }, + "@jest/transform": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.4.tgz", + "integrity": "sha512-6eRigvb+G6bs4kW5j1/y8wu4nCrmVuIe0epPBbiWaYlwawJ8yi1EIyK3d/btDqmBpN5GpN4YhR6iPPnDmkYdTA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^25.2.3", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.2.3", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.3", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^2.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.3.tgz", + "integrity": "sha512-6oLQwO9mKif3Uph3RX5J1i3S7X7xtDHWBaaaoeKw8hOzV6YUd0qDcYcHZ6QXMHDIzSr7zzrEa51o2Ovlj6AtKQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, "@sinonjs/commons": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz", - "integrity": "sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz", + "integrity": "sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ==", "dev": true, "requires": { "type-detect": "4.0.8" } }, - "@sinonjs/formatio": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", - "integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==", + "@types/babel__core": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", + "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==", "dev": true, "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "@types/babel__generator": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", + "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", "dev": true, "requires": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" + "@babel/types": "^7.0.0" } }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } }, - "@types/accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "@types/babel__traverse": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.9.tgz", + "integrity": "sha512-jEFQ8L1tuvPjOI8lnpaf73oCJe+aoxL6ygqSy6c8LcW98zaC+4mzWuQIRCEvKeCOu+lbqdXcg4Uqmm1S8AP1tw==", "dev": true, "requires": { - "@types/node": "*" + "@babel/types": "^7.3.0" } }, "@types/body-parser": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", - "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", "dev": true, "requires": { "@types/connect": "*", "@types/node": "*" } }, - "@types/chai": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.3.tgz", - "integrity": "sha512-VRw2xEGbll3ZiTQ4J02/hUjNqZoue1bMhoo2dgM2LXjDdyaq4q80HgBDHwpI0/VKlo4Eg+BavyQMv/NYgTetzA==", + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, - "@types/chai-as-promised": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.2.tgz", - "integrity": "sha512-PO2gcfR3Oxa+u0QvECLe1xKXOqYTzCmWf0FhLhjREoW3fPAVamjihL7v1MOVLJLsnAMdLcjkfrs01yvDMwVK4Q==", - "dev": true, - "requires": { - "@types/chai": "*" - } - }, "@types/connect": { - "version": "3.4.32", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", - "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", "dev": true, "requires": { "@types/node": "*" } }, - "@types/cookies": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.3.tgz", - "integrity": "sha512-NEkYn8pNsYZIxf3ZrjdPoeyueiPc0RbQClUpTwmdHkpmQQ8iDAlQYKpabuegHy7BJcqTteSTkhURMEs9ZxyEWg==", + "@types/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-invOmosX0DqbpA+cE2yoHGUlF/blyf7nB0OGYBBiH27crcVm5NmFaZkLP4Ta1hGaesckCi5lVLlydNJCxkTOSg==", "dev": true, "requires": { - "@types/connect": "*", - "@types/express": "*", - "@types/keygrip": "*", - "@types/node": "*" + "@types/express": "*" } }, "@types/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.1.tgz", - "integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==", + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.4.tgz", + "integrity": "sha512-DO1L53rGqIDUEvOjJKmbMEQ5Z+BM2cIEPy/eV3En+s166Gz+FeuzRerxcab757u/U4v4XF4RYrZPmqKa+aY/2w==", "dev": true, "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "*", + "@types/qs": "*", "@types/serve-static": "*" } }, "@types/express-serve-static-core": { - "version": "4.16.9", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz", - "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.3.tgz", + "integrity": "sha512-sHEsvEzjqN+zLbqP+8OXTipc10yH1QLR+hnr5uw29gi9AhCAAAdri8ClNV7iMdrJrIzXIQtlkPvq8tJGhj3QJQ==", "dev": true, "requires": { "@types/node": "*", @@ -277,66 +709,105 @@ } }, "@types/express-session": { - "version": "1.15.14", - "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.15.14.tgz", - "integrity": "sha512-7kVzFTT0Jy0zmUYDt9ik76XbcqyS9NalV4gn4eLwhk1nGQn+lS/HjPODhG3Oi/GBR2w1LQHUdkz/5KICYMACiw==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.0.tgz", + "integrity": "sha512-OQEHeBFE1UhChVIBhRh9qElHUvTp4BzKKHxMDkGHT7WuYk5eL93hPG7D8YAIkoBSbhNEY0RjreF15zn+U0eLjA==", "dev": true, "requires": { "@types/express": "*", "@types/node": "*" } }, - "@types/http-assert": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", - "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==", - "dev": true + "@types/form-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.5.0.tgz", + "integrity": "sha512-23/wYiuckYYtFpL+4RPWiWmRQH2BjFuqCUi2+N3amB1a1Drv+i/byTrGvlLwRVLFNAZbwpbQ7JvTK+VCAPMbcg==", + "dev": true, + "requires": { + "form-data": "*" + } }, - "@types/keygrip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.1.tgz", - "integrity": "sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg=", + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", "dev": true }, - "@types/koa": { - "version": "2.0.50", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.50.tgz", - "integrity": "sha512-TcgOD2lh0EISSadAk1DOBYw7kNoY9XdeB3vEMOKiDDaTMYm+V54nyPsU7Ulb/htb5OBIR79RgTeCWntCcophLw==", + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dev": true, "requires": { - "@types/accepts": "*", - "@types/cookies": "*", - "@types/http-assert": "*", - "@types/keygrip": "*", - "@types/koa-compose": "*", - "@types/node": "*" + "@types/istanbul-lib-coverage": "*" } }, - "@types/koa-compose": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.4.tgz", - "integrity": "sha512-ioou0rxkuWL+yBQYsHUQAzRTfVxAg8Y2VfMftU+Y3RA03/MzuFL0x/M2sXXj3PkfnENbHsjeHR1aMdezLYpTeA==", + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "25.1.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.4.tgz", + "integrity": "sha512-QDDY2uNAhCV7TMCITrxz+MRk1EizcsevzfeS6LykIlq2V1E5oO4wXG8V2ZEd9w7Snxeeagk46YbMgZ8ESHx3sw==", "dev": true, "requires": { - "@types/koa": "*" + "jest-diff": "^25.1.0", + "pretty-format": "^25.1.0" } }, + "@types/lru-cache": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-4.1.2.tgz", + "integrity": "sha512-ve2IoUJClE+4S/sG2zoLGEHP6DCvqgyz7UkHZdiICdQaAYRaCXsRWfJlbL8B0KvUyo9lgzD+oR0YSy4YikFyFQ==", + "dev": true + }, "@types/mime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", "dev": true }, - "@types/mocha": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", - "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", - "dev": true + "@types/multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-pVcPwuC0FbVcLhopJHx8Ro3WSXjvVvEpJMfy+DFAL/3DwNYAQH+hf/Vq+PqoS5kM4mng7L/4upzXhP/12yWh4w==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/mustache-express": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/mustache-express/-/mustache-express-1.2.1.tgz", + "integrity": "sha512-wZ/XqSpJjlOf7qI8ch/oaD/zHfC8K/TM+kSdL/CVdiPp1xu7DoI4jk/4vR4vkEYKeS2zV54q8Le9C0NebtvoSg==", + "dev": true, + "requires": { + "@types/lru-cache": "^4" + } }, "@types/node": { - "version": "12.7.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.8.tgz", - "integrity": "sha512-FMdVn84tJJdV+xe+53sYiZS4R5yn1mAIxfj+DVoNiQjTYz1+OYmjwEZr1ev9nU0axXwda0QDbYl06QHanRVH3A==", + "version": "13.9.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.8.tgz", + "integrity": "sha512-1WgO8hsyHynlx7nhP1kr0OFzsgKz5XDQL+Lfc3b1Q3qIln/n8cKD4m09NJ0+P1Rq7Zgnc7N0+SsMnoD1rEb0kA==", + "dev": true + }, + "@types/prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==", + "dev": true + }, + "@types/qs": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.1.tgz", + "integrity": "sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw==", "dev": true }, "@types/range-parser": { @@ -355,10 +826,10 @@ "@types/mime": "*" } }, - "@types/sinon": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.0.13.tgz", - "integrity": "sha512-d7c/C/+H/knZ3L8/cxhicHUiTDxdgap0b/aNJfsmLwFu/iOP17mdgbQsbHA3SJmrzsjD0l3UEE5SN4xxuz5ung==", + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", "dev": true }, "@types/validator": { @@ -367,10 +838,25 @@ "integrity": "sha512-GKF2VnEkMmEeEGvoo03ocrP9ySMuX1ypKazIYMlsjfslfBMhOAtC5dmEWKdJioW4lJN7MZRS88kalTsVClyQ9w==", "dev": true }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "abab": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", + "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", "dev": true }, "accepts": { @@ -383,70 +869,92 @@ "negotiator": "0.6.2" } }, + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dev": true, + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true + }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "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": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "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": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true - }, - "append-field": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz", - "integrity": "sha1-bdxY+gg8e8VF08WZWygwzCNm1Eo=", - "dev": true - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { - "default-require-extensions": "^2.0.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "archy": { + "append-field": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" }, "arg": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz", - "integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "argparse": { @@ -458,16 +966,40 @@ "sprintf-js": "~1.0.2" } }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", + "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 }, "asn1": { @@ -485,16 +1017,33 @@ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async/-/async-3.1.1.tgz", + "integrity": "sha512-X5Dj8hK1pJNC2Wzo2Rcp9FBVdJMGRR/S7V+lH46s8GVFhtbo5O4Le5GECCF/8PISVdkUA6mMPvgz7qTTD1rf1g==", "dev": true }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "aws-sign2": { @@ -504,21 +1053,127 @@ "dev": true }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", "dev": true }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "dev": true, + "requires": { + "follow-redirects": "1.5.10" + } + }, + "babel-jest": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.2.4.tgz", + "integrity": "sha512-+yDzlyJVWrqih9i2Cvjpt7COaN8vUwCsKGtxJLzg6I0xhxD54K8mvDUCliPKLufyzHh/c5C4MRj4Vk7VMjOjIg==", + "dev": true, + "requires": { + "@jest/transform": "^25.2.4", + "@jest/types": "^25.2.3", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^25.2.1", + "chalk": "^3.0.0", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.2.1.tgz", + "integrity": "sha512-HysbCQfJhxLlyxDbKcB2ucGYV0LjqK4h6dBoI3RtFuOxTiTWK6XGZMsHb0tGh8iJdV4hC6Z2GCHzVvDeh9i0lQ==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.2.1.tgz", + "integrity": "sha512-zXHJBM5iR8oEO4cvdF83AQqqJf3tJrXy3x8nfu2Nlqvn4cneg4Ca8M7cQvC5S9BzDDy1O0tZ9iXru9J6E3ym+A==", + "dev": true, + "requires": { + "@babel/plugin-syntax-bigint": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^25.2.1" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -529,12 +1184,6 @@ "tweetnacl": "^0.14.3" } }, - "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", - "dev": true - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -553,29 +1202,19 @@ "type-is": "~1.6.17" }, "dependencies": { - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "ms": "2.0.0" } }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", "dev": true } } @@ -589,62 +1228,68 @@ "concat-map": "0.0.1" } }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "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" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, - "buffer": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", - "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", "dev": true, "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" } }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "busboy": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", - "dev": true, "requires": { "dicer": "0.2.5", "readable-stream": "1.1.x" - }, - "dependencies": { - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } } }, "bytes": { @@ -653,27 +1298,28 @@ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true }, - "cache-content-type": { + "cache-base": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", - "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { - "mime-types": "^2.1.18", - "ylru": "^1.2.0" + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" } }, - "caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", - "dev": true, - "requires": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" - } + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true }, "camelcase": { "version": "5.3.1", @@ -681,142 +1327,75 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "check-error": "^1.0.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "chai-subset": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz", - "integrity": "sha1-pdDKFOMpp5WW7XAFi2ZGvWmIz+k=", + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, - "chakram": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/chakram/-/chakram-1.5.0.tgz", - "integrity": "sha1-PYsKiPdo3WraWSpSRmPMDcFKwc8=", - "dev": true, - "requires": { - "chai": "3.x.x", - "chai-as-promised": "5.x.x", - "chai-subset": "1.x.x", - "extend-object": "1.x.x", - "q": "1.x.x", - "request": "2.x.x", - "request-debug": "0.x.x", - "tv4": "1.x.x" - }, - "dependencies": { - "chai": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", - "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", - "dev": true, - "requires": { - "assertion-error": "^1.0.1", - "deep-eql": "^0.1.3", - "type-detect": "^1.0.0" - } - }, - "chai-as-promised": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-5.3.0.tgz", - "integrity": "sha1-CdekApCKpw39vq1T5YU/x50+8hw=", - "dev": true - }, - "deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "dev": true, - "requires": { - "type-detect": "0.1.1" - }, - "dependencies": { - "type-detect": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", - "dev": true - } - } - }, - "type-detect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", - "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", - "dev": true - } - } + "class-transformer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.2.3.tgz", + "integrity": "sha512-qsP+0xoavpOlJHuYsQJsN58HXSl8Jvveo+T37rEvCEeRfMWoytAyR0Ua/YsFgpM6AZYZ/og2PJwArwzJl1aXtQ==", + "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "has-flag": "^3.0.0" + "is-descriptor": "^0.1.0" } } } }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "class-transformer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.2.3.tgz", - "integrity": "sha512-qsP+0xoavpOlJHuYsQJsN58HXSl8Jvveo+T37rEvCEeRfMWoytAyR0Ua/YsFgpM6AZYZ/og2PJwArwzJl1aXtQ==", - "dev": true - }, "class-validator": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.10.1.tgz", - "integrity": "sha512-XC4drXqOzWUTmGExg7L3+n8V9u4HrnTFh3k8oFVH/B61O06ZyUZsZ3ONAC2EJ9nQl95nn8WOl3bo/n+u8Axm+w==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.11.1.tgz", + "integrity": "sha512-6CGdjwJLmKw+sQbK5ZDo1v1yTajkqfPOUDWSYVIlhUiCh6Phy8sAnMFE2XKHAcKAdoOz4jJUQhjPQWPYUuHxrA==", "dev": true, "requires": { "@types/validator": "10.11.3", "google-libphonenumber": "^3.1.6", - "validator": "11.1.0" + "validator": "12.0.0" } }, "cliui": { @@ -828,34 +1407,6 @@ "string-width": "^3.1.0", "strip-ansi": "^5.2.0", "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } } }, "co": { @@ -864,52 +1415,56 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, - "co-body": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/co-body/-/co-body-6.0.0.tgz", - "integrity": "sha512-9ZIcixguuuKIptnY8yemEOuhb71L/lLf+Rl5JfJEUiDNJk0e02MBt7BPxR2GEh5mw8dPthQYR4jPI/BnS1MQgw==", + "collect-v8-coverage": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz", + "integrity": "sha512-VKIhJgvk8E1W28m5avZ2Gv2Ruv5YiF56ug2oclvaG9md69BuZImMG2sk9g7QNKLUbtYAKQjXjYxbYZVUlMMKmQ==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "inflation": "^2.0.0", - "qs": "^6.5.2", - "raw-body": "^2.3.3", - "type-is": "^1.6.16" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "optional": true }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "concat-map": { @@ -921,64 +1476,42 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" - } - }, - "condense-newlines": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", - "integrity": "sha1-PemFVTE5R10yUCyDsC9gaE0kxV8=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-whitespace": "^0.3.0", - "kind-of": "^3.0.2" }, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { - "is-extendable": "^0.1.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "is-buffer": "^1.1.5" + "safe-buffer": "~5.1.0" } } } }, - "config-chain": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", - "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", - "dev": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "consolidate": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", - "dev": true, - "requires": { - "bluebird": "^3.1.1" - } - }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -986,14 +1519,6 @@ "dev": true, "requires": { "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } } }, "content-type": { @@ -1003,9 +1528,9 @@ "dev": true }, "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" @@ -1022,26 +1547,16 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, - "cookies": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.3.tgz", - "integrity": "sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "keygrip": "~1.0.3" - } - }, - "copy-to": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/copy-to/-/copy-to-2.0.1.tgz", - "integrity": "sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU=", + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, "copyfiles": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.1.1.tgz", - "integrity": "sha512-y6DZHve80whydXzBal7r70TBgKMPKesVRR1Sn/raUu7Jh/i7iSLSyGvYaq0eMJ/3Y/CKghwzjY32q1WzEnpp3Q==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.2.0.tgz", + "integrity": "sha512-iJbHJI+8OKqsq+4JF0rqgRkZzo++jqO6Wf4FUU1JM41cJF6JcY5968XyF4tm3Kkm7ZOMrqlljdm8N9oyY5raGw==", "dev": true, "requires": { "glob": "^7.0.5", @@ -1050,145 +1565,12 @@ "noms": "0.0.0", "through2": "^2.0.1", "yargs": "^13.2.4" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } } }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cors": { "version": "2.8.5", @@ -1200,54 +1582,57 @@ "vary": "^1" } }, - "cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } } } }, - "crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "requires": { - "buffer": "^5.1.0" - } - }, - "cross-env": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.0.tgz", - "integrity": "sha512-G/B6gtkjgthT8AP/xN1wdj5Xe18fVyk58JepK8GxpUbqcz3hyWxegocMbvnZK+KoTslwd0ACZ3woi/DVUdVjyQ==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0" - } + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true }, - "cross-spawn": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.0.tgz", - "integrity": "sha512-6U/8SMK2FBNnB21oQ4+6Nsodxanw1gTkntYA2zBdkFYFu3ZDx65P2ONEXGSvob/QS6REjVHQ9zxzdOafwFdstw==", + "cssstyle": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz", + "integrity": "sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA==", "dev": true, "requires": { - "path-key": "^3.1.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } } }, "dashdash": { @@ -1259,10 +1644,21 @@ "assert-plus": "^1.0.0" } }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + } + }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -1274,58 +1670,69 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "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": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "strip-bom": "^3.0.0" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } } } }, - "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" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "depd": { "version": "1.1.2", @@ -1339,42 +1746,42 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, "dicer": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", - "dev": true, "requires": { "readable-stream": "1.1.x", "streamsearch": "0.1.2" - }, - "dependencies": { - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } } }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, + "diff-sequences": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.1.tgz", + "integrity": "sha512-foe7dXnGlSh3jR1ovJmdv+77VQj98eKCHHwJPbZ2eEf0fHwKbkZicpPxEch9smZ+n2dnF6QFwkOQdLq9hpeJUg==", + "dev": true + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "requires": { + "webidl-conversions": "^4.0.2" + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1385,41 +1792,10 @@ "safer-buffer": "^2.1.0" } }, - "editorconfig": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", - "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", - "dev": true, - "requires": { - "commander": "^2.19.0", - "lru-cache": "^4.1.5", - "semver": "^5.6.0", - "sigmund": "^1.0.1" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "emoji-regex": { "version": "7.0.3", @@ -1433,56 +1809,15 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, - "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" - } - }, - "error-inject": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/error-inject/-/error-inject-1.0.0.tgz", - "integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc=", - "dev": true - }, - "es-abstract": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", - "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.0", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", - "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.0.0", - "string.prototype.trimright": "^2.0.0" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "once": "^1.4.0" } }, - "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 - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1495,16 +1830,35 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "escodegen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", + "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "etag": { @@ -1513,13 +1867,98 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { - "accepts": "~1.3.7", + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expect": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-25.2.4.tgz", + "integrity": "sha512-hfuPhPds4yOsZtIw4kwAg70r0hqGmpqekgA+VX7pf/3wZ6FY+xIOXZhNsPMMMsspYG/YIsbAiwqsdnD4Ht+bCA==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3", + "ansi-styles": "^4.0.0", + "jest-get-type": "^25.2.1", + "jest-matcher-utils": "^25.2.3", + "jest-message-util": "^25.2.4", + "jest-regex-util": "^25.2.1" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", "array-flatten": "1.1.1", "body-parser": "1.19.0", "content-disposition": "0.5.3", @@ -1551,47 +1990,47 @@ "vary": "~1.1.2" }, "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", "dev": true } } }, "express-session": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.16.2.tgz", - "integrity": "sha512-oy0sRsdw6n93E9wpCNWKRnSsxYnSDX9Dnr9mhZgqUEEorzcq5nshGYSZ4ZReHFhKQ80WI5iVUUSPW7u3GaKauw==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.0.tgz", + "integrity": "sha512-t4oX2z7uoSqATbMfsxWMbNjAL0T5zpvcJCk3Z9wnPPN7ibddhnmDZXHfEcoBMG2ojKXZoCyPMc5FbtK+G7SoDg==", "dev": true, "requires": { - "cookie": "0.3.1", + "cookie": "0.4.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~2.0.0", "on-headers": "~1.0.2", "parseurl": "~1.3.3", - "safe-buffer": "5.1.2", + "safe-buffer": "5.2.0", "uid-safe": "~2.1.5" }, "dependencies": { - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } }, "depd": { "version": "2.0.0", @@ -1600,9 +2039,9 @@ "dev": true }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", "dev": true } } @@ -1613,11 +2052,91 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "extend-object": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/extend-object/-/extend-object-1.0.0.tgz", - "integrity": "sha1-QlFPhAFdE1bK9Rh5ad+yvBvaCCM=", - "dev": true + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } }, "extsprintf": { "version": "1.3.0", @@ -1626,17 +2145,41 @@ "dev": true }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", "dev": true }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "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 + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.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" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -1652,25 +2195,17 @@ "unpipe": "~1.0.0" }, "dependencies": { - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } } } }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -1680,60 +2215,20 @@ "locate-path": "^3.0.0" } }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "dev": true, "requires": { - "is-buffer": "~2.0.3" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", - "dev": true - } + "debug": "=3.1.0" } }, - "foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", - "dev": true, - "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true }, "forever-agent": { "version": "0.6.1", @@ -1742,13 +2237,12 @@ "dev": true }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, @@ -1758,6 +2252,15 @@ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", "dev": true }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -1769,10 +2272,17 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", "dev": true }, "get-caller-file": { @@ -1781,29 +2291,21 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-paths": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/get-paths/-/get-paths-0.0.7.tgz", - "integrity": "sha512-0wdJt7C1XKQxuCgouqd+ZvLJ56FQixKoki9MrFaO4EriqzXOiH9gbukaDE1ou08S8Ns3/yDzoBAISNPqj6e6tA==", + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "pify": "^4.0.1" - }, - "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - } + "pump": "^3.0.0" } }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -1814,9 +2316,9 @@ } }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1833,41 +2335,34 @@ "dev": true }, "google-libphonenumber": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.5.tgz", - "integrity": "sha512-Y0r7MFCI11UDLn0KaMPBEInhROyIOkWkQIyvWMFVF2I+h+sHE3vbl5a7FVe39td6u/w+nlKDdUMP9dMOZyv+2Q==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.8.tgz", + "integrity": "sha512-iWs1KcxOozmKQbCeGjvU0M7urrkNjBYOSBtb819RjkUNJHJLfn7DADKkKwdJTOMPLcLOE11/4h/FyFwJsTiwLg==", "dev": true }, "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true }, "handlebars": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.0.tgz", - "integrity": "sha512-xkRtOt3/3DzTKMOt3xahj2M/EqNhY988T+imYSlMgs5fVhLN2fmKVVj0LtEGmb+3UUYV5Qmm1052Mm3dIQxOvw==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz", + "integrity": "sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==", "dev": true, "requires": { "neo-async": "^2.6.0", "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "har-schema": { @@ -1886,115 +2381,96 @@ "har-schema": "^2.0.0" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "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": { + "has-value": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "is-stream": "^1.0.1" - }, - "dependencies": { - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - } + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" } }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", - "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", - "dev": true - }, - "http-assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.1.tgz", - "integrity": "sha512-rdw7q6GTlibqVVbXr0CKelfV5iY8G2HqEUkhSk297BMbSpSL8crXC+9rjKoMcZZEsksX30le6f/4ul4E28gegw==", + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "deep-equal": "~1.0.1", - "http-errors": "~1.7.2" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } } } }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "dev": true, "requires": { - "depd": "1.1.1", + "depd": "~1.1.2", "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" }, "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true } } @@ -2010,6 +2486,18 @@ "sshpk": "^1.7.0" } }, + "http-status-codes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.4.0.tgz", + "integrity": "sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ==", + "dev": true + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2019,11 +2507,15 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } }, "imurmurhash": { "version": "0.1.4", @@ -2031,12 +2523,6 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "inflation": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.0.0.tgz", - "integrity": "sha1-i0F+R8KPklpFEz2RTKH9OJEH8w8=", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2047,27 +2533,41 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", "dev": true }, "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true }, - "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-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } }, "is-buffer": { "version": "1.1.6", @@ -2075,23 +2575,53 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } }, - "is-class-hotfix": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/is-class-hotfix/-/is-class-hotfix-0.0.6.tgz", - "integrity": "sha512-0n+pzCC6ICtVr/WXnN2f03TK/3BfXY7me4cjCAqT8TYXEl0+JBRoqBo94JJHXcyDSLUeWbNX8Fvy5g5RJdAstQ==", - "dev": true + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } }, "is-extendable": { "version": "0.1.1", @@ -2105,40 +2635,32 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "is-generator-function": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", - "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==", + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.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 }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "isobject": "^3.0.1" } }, - "is-type-of": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-type-of/-/is-type-of-1.2.1.tgz", - "integrity": "sha512-uK0kyX9LZYhSDS7H2sVJQJop1UnWPWmo5RvR3q2kFH6AUHYs7sOrVg0b4nyBHw29kRRNFofYN/JbHZDlHiItTA==", - "dev": true, - "requires": { - "core-util-is": "^1.0.2", - "is-class-hotfix": "~0.0.6", - "isstream": "~0.1.2" - } + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true }, "is-typedarray": { "version": "1.0.0", @@ -2146,17 +2668,23 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "is-whitespace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", - "integrity": "sha1-Fjnssb4DauxppUy7QBz77XEUq38=", + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, + "is-wsl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", + "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", + "dev": true, + "optional": true + }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "isexe": { "version": "2.0.0", @@ -2164,6 +2692,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -2171,81 +2705,45 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true }, - "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", "dev": true, "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" } }, "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" } }, "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", "dev": true, "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", + "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" }, "dependencies": { @@ -2263,460 +2761,878 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, "istanbul-reports": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", - "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", - "dev": true, - "requires": { - "handlebars": "^4.1.2" - } - }, - "js-beautify": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.10.2.tgz", - "integrity": "sha512-ZtBYyNUYJIsBWERnQP0rPN9KjkrDfJcMjuVGcvXOUJrD1zmOGwhRwQ4msG+HJ+Ni/FA7+sRQEMYVzdTQDvnzvQ==", - "dev": true, - "requires": { - "config-chain": "^1.1.12", - "editorconfig": "^0.15.3", - "glob": "^7.1.3", - "mkdirp": "~0.5.1", - "nopt": "~4.0.1" - }, - "dependencies": { - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-Vm9xwCiQ8t2cNNnckyeAV0UdxKpcQUz4nMxsBvIu8n2kmPSiyb5uaF/8LpmKr+yqL/MdOXaX2Nmdo4Qyxium9Q==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/json/-/json-9.0.6.tgz", - "integrity": "sha1-eXLCpaSKQmeNsnMMfCxO5uTiRYU=", - "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": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "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-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "jest": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-25.2.4.tgz", + "integrity": "sha512-Lu4LXxf4+durzN/IFilcAoQSisOwgHIXgl9vffopePpSSwFqfj1Pj4y+k3nL8oTbnvjxgDIsEcepy6he4bWqnQ==", "dev": true, "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "just-extend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", - "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", - "dev": true - }, - "kcors": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/kcors/-/kcors-2.2.2.tgz", - "integrity": "sha512-rIqbKa2S0gT0wC/790jsQM6hNpABHBNWQ7+XYS1xJV6zOGxlanW+RtCmlDn6wPZsGpRk371yy8abfBgl2OTavg==", - "dev": true - }, - "keygrip": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.3.tgz", - "integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g==", - "dev": true - }, - "koa": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.8.2.tgz", - "integrity": "sha512-q1uZOgpl3wjr5FS/tjbABJ8lA5+NeKa9eq7QyBP5xxgOBwJN4iBrMEgO3LroE51lrIw3BsO0WZZ0Yi6giSiMDw==", - "dev": true, - "requires": { - "accepts": "^1.3.5", - "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", - "cookies": "~0.7.1", - "debug": "~3.1.0", - "delegates": "^1.0.0", - "depd": "^1.1.2", - "destroy": "^1.0.4", - "encodeurl": "^1.0.2", - "error-inject": "^1.0.0", - "escape-html": "^1.0.3", - "fresh": "~0.5.2", - "http-assert": "^1.3.0", - "http-errors": "^1.6.3", - "is-generator-function": "^1.0.7", - "koa-compose": "^4.1.0", - "koa-convert": "^1.2.0", - "koa-is-json": "^1.0.0", - "on-finished": "^2.3.0", - "only": "~0.0.2", - "parseurl": "^1.3.2", - "statuses": "^1.5.0", - "type-is": "^1.6.16", - "vary": "^1.1.2" + "@jest/core": "^25.2.4", + "import-local": "^3.0.2", + "jest-cli": "^25.2.4" }, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "ms": "2.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "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 + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "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 }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true + "jest-cli": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-25.2.4.tgz", + "integrity": "sha512-zeY2pRDWKj2LZudIncvvguwLMEdcnJqc2jJbwza1beqi80qqLvkPF/BjbFkK2sIV3r+mfTJS+7ITrvK6pCdRjg==", + "dev": true, + "requires": { + "@jest/core": "^25.2.4", + "@jest/test-result": "^25.2.4", + "@jest/types": "^25.2.3", + "chalk": "^3.0.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^25.2.4", + "jest-util": "^25.2.3", + "jest-validate": "^25.2.3", + "prompts": "^2.0.1", + "realpath-native": "^2.0.0", + "yargs": "^15.3.1" + } }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - } - } - }, - "koa-bodyparser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/koa-bodyparser/-/koa-bodyparser-4.2.1.tgz", - "integrity": "sha512-UIjPAlMZfNYDDe+4zBaOAUKYqkwAGcIU6r2ARf1UOXPAlfennQys5IiShaVeNf7KkVBlf88f2LeLvBFvKylttw==", - "dev": true, - "requires": { - "co-body": "^6.0.0", - "copy-to": "^2.0.1" - } - }, - "koa-compose": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", - "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", - "dev": true - }, - "koa-convert": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz", - "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=", - "dev": true, - "requires": { - "co": "^4.6.0", - "koa-compose": "^3.0.0" - }, - "dependencies": { - "koa-compose": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", - "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "any-promise": "^1.1.0" + "p-locate": "^4.1.0" } - } - } - }, - "koa-is-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz", - "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ=", - "dev": true - }, - "koa-multer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/koa-multer/-/koa-multer-1.0.2.tgz", - "integrity": "sha512-0kFzN4atVd+9oiG+4fYxQ9S2T3dPhKNvmhITIY606Qn9wLEmfhW0DhSpOzRYhddN//4rh/TCK95TMtflmFa5lA==", - "dev": true, - "requires": { - "multer": "1.3.0" - }, - "dependencies": { - "multer": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.3.0.tgz", - "integrity": "sha1-CSsmcPaEb6SRSWXvyM+Uwg/sbNI=", + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "append-field": "^0.1.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.0", - "mkdirp": "^0.5.1", - "object-assign": "^3.0.0", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "p-limit": "^2.2.0" } }, - "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true - } - } - }, - "koa-router": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/koa-router/-/koa-router-7.4.0.tgz", - "integrity": "sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "http-errors": "^1.3.1", - "koa-compose": "^3.0.0", - "methods": "^1.0.1", - "path-to-regexp": "^1.1.1", - "urijs": "^1.19.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "ms": "2.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "koa-compose": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", - "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", + "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" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "requires": { - "any-promise": "^1.1.0" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.1" } }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "yargs-parser": { + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz", + "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==", "dev": true, "requires": { - "isarray": "0.0.1" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } }, - "koa-send": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.0.tgz", - "integrity": "sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ==", + "jest-changed-files": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.2.3.tgz", + "integrity": "sha512-EFxy94dvvbqRB36ezIPLKJ4fDIC+jAdNs8i8uTwFpaXd6H3LVc3ova1lNS4ZPWk09OCR2vq5kSdSQgar7zMORg==", "dev": true, "requires": { - "debug": "^3.1.0", - "http-errors": "^1.6.3", - "mz": "^2.7.0", - "resolve-path": "^1.4.0" + "@jest/types": "^25.2.3", + "execa": "^3.2.0", + "throat": "^5.0.0" }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", "dev": true, "requires": { - "ms": "^2.1.1" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "execa": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", "dev": true }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true } } }, - "koa-session": { - "version": "5.12.3", - "resolved": "https://registry.npmjs.org/koa-session/-/koa-session-5.12.3.tgz", - "integrity": "sha512-r1vRerk6z+J54TtgXPG+oYGj1vwrd889bz55hS7VykWvbtmPNb0q/N7arOHGV8dnstlRyZPP3xCJL20wIf9LwA==", + "jest-config": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.2.4.tgz", + "integrity": "sha512-fxy3nIpwJqOUQJRVF/q+pNQb6dv5b9YufOeCbpPZJ/md1zXpiupbhfehpfODhnKOfqbzSiigtSLzlWWmbRxnqQ==", "dev": true, "requires": { - "crc": "^3.4.4", - "debug": "^3.1.0", - "is-type-of": "^1.0.0", - "uuid": "^3.3.2" + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^25.2.4", + "@jest/types": "^25.2.3", + "babel-jest": "^25.2.4", + "chalk": "^3.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "jest-environment-jsdom": "^25.2.4", + "jest-environment-node": "^25.2.4", + "jest-get-type": "^25.2.1", + "jest-jasmine2": "^25.2.4", + "jest-regex-util": "^25.2.1", + "jest-resolve": "^25.2.3", + "jest-util": "^25.2.3", + "jest-validate": "^25.2.3", + "micromatch": "^4.0.2", + "pretty-format": "^25.2.3", + "realpath-native": "^2.0.0" + } + }, + "jest-diff": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.2.3.tgz", + "integrity": "sha512-VtZ6LAQtaQpFsmEzps15dQc5ELbJxy4L2DOSo2Ev411TUEtnJPkAMD7JneVypeMJQ1y3hgxN9Ao13n15FAnavg==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "diff-sequences": "^25.2.1", + "jest-get-type": "^25.2.1", + "pretty-format": "^25.2.3" + } + }, + "jest-docblock": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.2.3.tgz", + "integrity": "sha512-d3/tmjLLrH5fpRGmIm3oFa3vOaD/IjPxtXVOrfujpfJ9y1tCDB1x/tvunmdOVAyF03/xeMwburl6ITbiQT1mVA==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.2.3.tgz", + "integrity": "sha512-RTlmCjsBDK2c9T5oO4MqccA3/5Y8BUtiEy7OOQik1iyCgdnNdHbI0pNEpyapZPBG0nlvZ4mIu7aY6zNUvLraAQ==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.1", + "jest-util": "^25.2.3", + "pretty-format": "^25.2.3" + } + }, + "jest-environment-jsdom": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.2.4.tgz", + "integrity": "sha512-5dm+tNwrLmhELdjAwiQnVGf/U9iFMWdTL4/wyrMg2HU6RQnCiuxpWbIigLHUhuP1P2Ak0F4k3xhjrikboKyShA==", + "dev": true, + "requires": { + "@jest/environment": "^25.2.4", + "@jest/fake-timers": "^25.2.4", + "@jest/types": "^25.2.3", + "jest-mock": "^25.2.3", + "jest-util": "^25.2.3", + "jsdom": "^15.2.1" + } + }, + "jest-environment-node": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.2.4.tgz", + "integrity": "sha512-Jkc5Y8goyXPrLRHnrUlqC7P4o5zn2m4zw6qWoRJ59kxV1f2a5wK+TTGhrhCwnhW/Ckpdl/pm+LufdvhJkvJbiw==", + "dev": true, + "requires": { + "@jest/environment": "^25.2.4", + "@jest/fake-timers": "^25.2.4", + "@jest/types": "^25.2.3", + "jest-mock": "^25.2.3", + "jest-util": "^25.2.3", + "semver": "^6.3.0" + } + }, + "jest-get-type": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", + "dev": true + }, + "jest-haste-map": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.3.tgz", + "integrity": "sha512-pAP22OHtPr4qgZlJJFks2LLgoQUr4XtM1a+F5UaPIZNiCRnePA0hM3L7aiJ0gzwiNIYwMTfKRwG/S1L28J3A3A==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.3", + "jest-worker": "^25.2.1", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7", + "which": "^2.0.2" + } + }, + "jest-jasmine2": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.2.4.tgz", + "integrity": "sha512-juoKrmNmLwaheNbAg71SuUF9ovwUZCFNTpKVhvCXWk+SSeORcIUMptKdPCoLXV3D16htzhTSKmNxnxSk4SrTjA==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^25.2.4", + "@jest/source-map": "^25.2.1", + "@jest/test-result": "^25.2.4", + "@jest/types": "^25.2.3", + "chalk": "^3.0.0", + "co": "^4.6.0", + "expect": "^25.2.4", + "is-generator-fn": "^2.0.0", + "jest-each": "^25.2.3", + "jest-matcher-utils": "^25.2.3", + "jest-message-util": "^25.2.4", + "jest-runtime": "^25.2.4", + "jest-snapshot": "^25.2.4", + "jest-util": "^25.2.3", + "pretty-format": "^25.2.3", + "throat": "^5.0.0" + } + }, + "jest-leak-detector": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.2.3.tgz", + "integrity": "sha512-yblCMPE7NJKl7778Cf/73yyFWAas5St0iiEBwq7RDyaz6Xd4WPFnPz2j7yDb/Qce71A1IbDoLADlcwD8zT74Aw==", + "dev": true, + "requires": { + "jest-get-type": "^25.2.1", + "pretty-format": "^25.2.3" + } + }, + "jest-matcher-utils": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.2.3.tgz", + "integrity": "sha512-ZmiXiwQRVM9MoKjGMP5YsGGk2Th5ncyRxfXKz5AKsmU8m43kgNZirckVzaP61MlSa9LKmXbevdYqVp1ZKAw2Rw==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "jest-diff": "^25.2.3", + "jest-get-type": "^25.2.1", + "pretty-format": "^25.2.3" + } + }, + "jest-message-util": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.4.tgz", + "integrity": "sha512-9wWMH3Bf+GVTv0GcQLmH/FRr0x0toptKw9TA8U5YFLVXx7Tq9pvcNzTyJrcTJ+wLqNbMPPJlJNft4MnlcrtF5Q==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^25.2.4", + "@jest/types": "^25.2.3", + "@types/stack-utils": "^1.0.1", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.2.3.tgz", + "integrity": "sha512-xlf+pyY0j47zoCs8zGGOGfWyxxLximE8YFOfEK8s4FruR8DtM/UjNj61um+iDuMAFEBDe1bhCXkqiKoCmWjJzg==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-regex-util": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", + "dev": true + }, + "jest-resolve": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.2.3.tgz", + "integrity": "sha512-1vZMsvM/DBH258PnpUNSXIgtzpYz+vCVCj9+fcy4akZl4oKbD+9hZSlfe9RIDpU0Fc28ozHQrmwX3EqFRRIHGg==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3", + "browser-resolve": "^1.11.3", + "chalk": "^3.0.0", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "jest-resolve-dependencies": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.2.4.tgz", + "integrity": "sha512-qhUnK4PfNHzNdca7Ub1mbAqE0j5WNyMTwxBZZJjQlUrdqsiYho/QGK65FuBkZuSoYtKIIqriR9TpGrPEc3P5Gg==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3", + "jest-regex-util": "^25.2.1", + "jest-snapshot": "^25.2.4" + } + }, + "jest-runner": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.2.4.tgz", + "integrity": "sha512-5xaIfqqxck9Wg2CV4b9KmJtf/sWO7zWQx7O+34GCLGPzoPcVmB3mZtdrQI1/jS3Reqjru9ycLjgLHSf6XoxRqA==", + "dev": true, + "requires": { + "@jest/console": "^25.2.3", + "@jest/environment": "^25.2.4", + "@jest/test-result": "^25.2.4", + "@jest/types": "^25.2.3", + "chalk": "^3.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.3", + "jest-config": "^25.2.4", + "jest-docblock": "^25.2.3", + "jest-haste-map": "^25.2.3", + "jest-jasmine2": "^25.2.4", + "jest-leak-detector": "^25.2.3", + "jest-message-util": "^25.2.4", + "jest-resolve": "^25.2.3", + "jest-runtime": "^25.2.4", + "jest-util": "^25.2.3", + "jest-worker": "^25.2.1", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + } + }, + "jest-runtime": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.2.4.tgz", + "integrity": "sha512-6ehOUizgIghN+aV5YSrDzTZ+zJ9omgEjJbTHj3Jqes5D52XHfhzT7cSfdREwkNjRytrR7mNwZ7pRauoyNLyJ8Q==", + "dev": true, + "requires": { + "@jest/console": "^25.2.3", + "@jest/environment": "^25.2.4", + "@jest/source-map": "^25.2.1", + "@jest/test-result": "^25.2.4", + "@jest/transform": "^25.2.4", + "@jest/types": "^25.2.3", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.3", + "jest-config": "^25.2.4", + "jest-haste-map": "^25.2.3", + "jest-message-util": "^25.2.4", + "jest-mock": "^25.2.3", + "jest-regex-util": "^25.2.1", + "jest-resolve": "^25.2.3", + "jest-snapshot": "^25.2.4", + "jest-util": "^25.2.3", + "jest-validate": "^25.2.3", + "realpath-native": "^2.0.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.3.1" }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "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 - } - } - }, - "koa-views": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/koa-views/-/koa-views-6.2.1.tgz", - "integrity": "sha512-wU3tw48KuskaJg0x1JZmC73UWGgVmh6B54HuHDzTrkavig+dBI0NbFeGiWsaOCZREQkaTfIVXSPSByDant71ew==", - "dev": true, - "requires": { - "consolidate": "0.15.1", - "debug": "^4.1.0", - "get-paths": "0.0.7", - "koa-send": "^5.0.0", - "mz": "^2.4.0", - "pretty": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "ms": "^2.1.1" + "locate-path": "^5.0.0", + "path-exists": "^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==", + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "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" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.1" + } + }, + "yargs-parser": { + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz", + "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, + "jest-serializer": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", + "dev": true + }, + "jest-snapshot": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.2.4.tgz", + "integrity": "sha512-nIwpW7FZCq5p0AE3Oyqyb6jL0ENJixXzJ5/CD/XRuOqp3gS5OM3O/k+NnTrniCXxPFV4ry6s9HNfiPQBi0wcoA==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^25.2.3", + "@types/prettier": "^1.19.0", + "chalk": "^3.0.0", + "expect": "^25.2.4", + "jest-diff": "^25.2.3", + "jest-get-type": "^25.2.1", + "jest-matcher-utils": "^25.2.3", + "jest-message-util": "^25.2.4", + "jest-resolve": "^25.2.3", + "make-dir": "^3.0.0", + "natural-compare": "^1.4.0", + "pretty-format": "^25.2.3", + "semver": "^6.3.0" + } + }, + "jest-util": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.3.tgz", + "integrity": "sha512-7tWiMICVSo9lNoObFtqLt9Ezt5exdFlWs5fLe1G4XLY2lEbZc814cw9t4YHScqBkWMfzth8ASHKlYBxiX2rdCw==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3", + "chalk": "^3.0.0", + "is-ci": "^2.0.0", + "make-dir": "^3.0.0" + } + }, + "jest-validate": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.2.3.tgz", + "integrity": "sha512-GObn91jzU0B0Bv4cusAwjP6vnWy78hJUM8MOSz7keRfnac/ZhQWIsUjvk01IfeXNTemCwgR57EtdjQMzFZGREg==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.1", + "leven": "^3.1.0", + "pretty-format": "^25.2.3" + } + }, + "jest-watcher": { + "version": "25.2.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.2.4.tgz", + "integrity": "sha512-p7g7s3zqcy69slVzQYcphyzkB2FBmJwMbv6k6KjI5mqd6KnUnQPfQVKuVj2l+34EeuxnbXqnrjtUFmxhcL87rg==", + "dev": true, + "requires": { + "@jest/test-result": "^25.2.4", + "@jest/types": "^25.2.3", + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "jest-util": "^25.2.3", + "string-length": "^3.1.0" + } + }, + "jest-worker": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz", + "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^7.1.0", + "acorn-globals": "^4.3.2", + "array-equal": "^1.0.0", + "cssom": "^0.4.1", + "cssstyle": "^2.0.0", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.1", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.2.0", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.7", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^7.0.0", + "xml-name-validator": "^3.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/json/-/json-9.0.6.tgz", + "integrity": "sha1-eXLCpaSKQmeNsnMMfCxO5uTiRYU=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "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-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", + "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -2725,14 +3641,6 @@ "requires": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" - }, - "dependencies": { - "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 - } } }, "lodash": { @@ -2741,26 +3649,26 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true }, "lolex": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", - "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", - "dev": true + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } }, "lru-cache": { "version": "5.1.1", @@ -2772,34 +3680,48 @@ } }, "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", "dev": true, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - } + "semver": "^6.0.0" } }, "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "merge-descriptors": { "version": "1.0.1", @@ -2807,22 +3729,11 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "methods": { "version": "1.1.2", @@ -2830,6 +3741,16 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", "dev": true }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2837,20 +3758,24 @@ "dev": true }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", - "dev": true + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "dev": true, + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.43.0" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -2860,99 +3785,39 @@ } }, "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { - "minimist": "0.0.8" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "mocha": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.1.tgz", - "integrity": "sha512-VCcWkLHwk79NYQc8cxhkmI8IigTIhsCwZ6RTxQsqK6go4UvEhzJkYuHm8B2YtlSxcYq2fY+ucr4JBwoD6ci80A==", - "dev": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "2.2.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "ms": "2.1.1", - "node-environment-flags": "1.0.5", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.0", - "yargs-parser": "13.1.1", - "yargs-unparser": "1.6.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "isexe": "^2.0.0" + "is-plain-object": "^2.0.4" } } } }, + "mkdirp": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", + "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "requires": { + "minimist": "^1.2.5" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2963,7 +3828,6 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", - "dev": true, "requires": { "append-field": "^1.0.0", "busboy": "^0.2.11", @@ -2973,20 +3837,12 @@ "on-finished": "^2.3.0", "type-is": "^1.6.4", "xtend": "^4.0.0" - }, - "dependencies": { - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=", - "dev": true - } } }, "mustache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.1.0.tgz", - "integrity": "sha512-3Bxq1R5LBZp7fbFPZzFe5WN4s0q3+gxZaZuZVY+QctYJiCiVgXHOTIC0/HgZuOPFt/6BQcx5u0H2CUOxT/RoGQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.2.1.tgz", + "integrity": "sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA==", "dev": true }, "mustache-express": { @@ -2998,27 +3854,33 @@ "async": "~3.1.0", "lru-cache": "~5.1.1", "mustache": "^3.1.0" - }, - "dependencies": { - "async": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz", - "integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ==", - "dev": true - } } }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" } }, + "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", @@ -3031,46 +3893,50 @@ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, - "nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", "dev": true }, - "nise": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.2.tgz", - "integrity": "sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA==", + "node-notifier": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz", + "integrity": "sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw==", "dev": true, + "optional": true, "requires": { - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^4.1.0", - "path-to-regexp": "^1.7.0" + "growly": "^1.3.0", + "is-wsl": "^2.1.1", + "semver": "^6.3.0", + "shellwords": "^0.1.1", + "which": "^1.3.1" }, "dependencies": { - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, + "optional": true, "requires": { - "isarray": "0.0.1" + "isexe": "^2.0.0" } } } }, - "node-environment-flags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", - "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, "noms": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", @@ -3092,268 +3958,94 @@ "isarray": "0.0.1", "string_decoder": "~0.10.x" } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true } } }, - "normalize-package-data": { - "version": "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==", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "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" + "path-key": "^2.0.0" } }, - "nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", - "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "is-descriptor": "^0.1.0" } }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "is-buffer": "^1.1.5" } } } }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", - "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.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "isobject": "^3.0.0" } }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "isobject": "^3.0.1" } }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, "requires": { "ee-first": "1.1.1" } @@ -3372,11 +4064,14 @@ "wrappy": "1" } }, - "only": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", - "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=", - "dev": true + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } }, "optimist": { "version": "0.6.1", @@ -3386,34 +4081,46 @@ "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" } }, + "p-each-series": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", + "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -3434,17 +4141,11 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true }, "parseurl": { "version": "1.3.3", @@ -3452,15 +4153,27 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "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", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz", - "integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "path-parse": { @@ -3475,110 +4188,153 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "^4.0.0" }, "dependencies": { "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } - } - } - }, - "pretty": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", - "integrity": "sha1-rbx5YLe7/iiaVX3F9zdhmiINBqU=", - "dev": true, - "requires": { - "condense-newlines": "^0.2.1", - "extend-shallow": "^2.0.1", - "js-beautify": "^1.6.12" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "p-locate": "^4.1.0" } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true } } }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, - "proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "pretty-format": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.3.tgz", + "integrity": "sha512-IP4+5UOAVGoyqC/DiomOeHBUKN6q00gfyT2qpAsRH64tgOKB2yF7FHJXC18OCiU0/YFierACup/zdCOWw0F/0w==", + "dev": true, + "requires": { + "@jest/types": "^25.2.3", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "prompts": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", + "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.4" + } + }, "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", "dev": true, "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" + "ipaddr.js": "1.9.1" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "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 }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", + "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==", "dev": true }, "random-bytes": { @@ -3603,76 +4359,68 @@ "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" - }, - "dependencies": { - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - } } }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "es6-error": "^4.0.1" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -3682,7 +4430,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -3692,32 +4440,70 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" }, "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } } } }, - "request-debug": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/request-debug/-/request-debug-0.2.0.tgz", - "integrity": "sha1-/AVOyBcYGwTKQaBSwTb2HEirr3g=", + "request-promise-core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "request-promise-native": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", + "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", "dev": true, "requires": { - "stringify-clone": "^1.0.0" + "request-promise-core": "1.1.3", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, "require-directory": { @@ -3733,55 +4519,230 @@ "dev": true }, "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", "dev": true, "requires": { "path-parse": "^1.0.6" } }, - "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 - }, - "resolve-path": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz", - "integrity": "sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc=", + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { - "http-errors": "~1.6.2", - "path-is-absolute": "1.0.1" + "resolve-from": "^5.0.0" } }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, "rimraf": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", - "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", "dev": true }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dev": true, + "requires": { + "xmlchars": "^2.1.1" + } + }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "send": { @@ -3805,42 +4766,28 @@ "statuses": "~1.5.0" }, "dependencies": { - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true } } }, @@ -3862,10 +4809,33 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, "shebang-command": { @@ -3883,137 +4853,197 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", - "dev": true + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "sinon": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", - "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { - "@sinonjs/commons": "^1.4.0", - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/samsam": "^3.3.3", - "diff": "^3.5.0", - "lolex": "^4.2.0", - "nise": "^1.5.2", - "supports-color": "^5.5.0" + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" }, "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "has-flag": "^3.0.0" + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, - "sinon-chai": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.3.0.tgz", - "integrity": "sha512-r2JhDY7gbbmh5z3Q62pNbrjxZdOAjpsqW/8yxAZRSqLZqowmfGZPGUZPFf3UX36NLis0cv8VEM5IJh9HgkSOAA==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } } } }, - "spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" + "kind-of": "^3.2.0" }, "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "glob": "^7.1.3" + "is-buffer": "^1.1.5" } } } }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "dev": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -4037,210 +5067,175 @@ "tweetnacl": "~0.14.0" } }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", "dev": true }, - "streamsearch": { + "static-extend": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } } }, - "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "string-length": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", + "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "astral-regex": "^1.0.0", + "strip-ansi": "^5.2.0" } }, - "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } } }, - "stringify-clone": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stringify-clone/-/stringify-clone-1.1.1.tgz", - "integrity": "sha1-MJojX7Ts/M19OI2+GLqQT6yvQzs=", + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" } }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "template-url": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/template-url/-/template-url-1.0.0.tgz", "integrity": "sha1-2UVr7nDKxmF7Rip7CNsp+4E6Cwk=" }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", "dev": true, "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.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" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "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": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" } }, - "thenify": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", - "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { - "any-promise": "^1.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", - "dev": true, - "requires": { - "thenify": ">= 3.1.0 < 4" - } + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true }, "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "^2.1.5", + "readable-stream": "~2.3.6", "xtend": "~4.0.1" }, "dependencies": { @@ -4251,9 +5246,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", - "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -4261,26 +5256,73 @@ "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", + "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "is-buffer": "^1.1.5" } } } }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } }, "toidentifier": { "version": "1.0.0", @@ -4289,143 +5331,72 @@ "dev": true }, "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", "dev": true, "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" } }, - "ts-node": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.4.1.tgz", - "integrity": "sha512-5LpRN+mTiCs7lI5EtbXmF/HfMeCjzt7DH9CZwtkr6SywStrNQC723wG+aOWFiLNn7zT3kD/RnFqi3ZUfr4l5Qw==", + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", "dev": true, "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - }, - "dependencies": { - "diff": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", - "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", - "dev": true - } + "punycode": "^2.1.0" } }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", - "dev": true - }, - "tslint": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.0.tgz", - "integrity": "sha512-2vqIvkMHbnx8acMogAERQ/IuINOq6DFqgF8/VDvhEkBqQh/x6SP0Y+OHnKth9/ZcHQSroOZwUQSN18v8KKF0/g==", + "ts-jest": { + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.3.0.tgz", + "integrity": "sha512-qH/uhaC+AFDU9JfAueSr0epIFJkGMvUPog4FxSEVAtPOur1Oni5WBJMiQIkfHvc7PviVRsnlVLLY2I6221CQew==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "mkdirp": "1.x", + "resolve": "1.x", + "semver": "6.x", + "yargs-parser": "^18.1.1" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true - }, - "diff": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", - "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "mkdirp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz", + "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==", "dev": true }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "yargs-parser": { + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz", + "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "ts-node": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.8.1.tgz", + "integrity": "sha512-10DE9ONho06QORKAaCBpPiFCdW+tZJuY/84tyypGtl6r+/C7Asq0dhqbRZURuUlLQtZxxDvT8eoj8cGW0ha6Bg==", "dev": true, "requires": { - "tslib": "^1.8.1" + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "3.1.1" } }, "tunnel-agent": { @@ -4437,29 +5408,37 @@ "safe-buffer": "^5.0.1" } }, - "tv4": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", - "integrity": "sha1-0CDIRvrdUMhVq7JeuuzGj8EPeWM=", - "dev": true - }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -4468,8 +5447,16 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } }, "typedi": { "version": "0.8.0", @@ -4478,29 +5465,20 @@ "dev": true }, "typescript": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", - "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", "dev": true }, "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz", + "integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==", "dev": true, "optional": true, "requires": { - "commander": "~2.20.0", + "commander": "~2.20.3", "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } } }, "uid-safe": { @@ -4512,12 +5490,70 @@ "random-bytes": "~1.0.0" } }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -4527,17 +5563,22 @@ "punycode": "^2.1.0" } }, - "urijs": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.1.tgz", - "integrity": "sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg==", + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", @@ -4546,25 +5587,34 @@ "dev": true }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "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==", + "v8-to-istanbul": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.3.tgz", + "integrity": "sha512-sAjOC+Kki6aJVbUOXJbcR0MnbfjvBzwKZazEJymA2IX49uoOdEdk+4fBq5cXgYgiyKtAyrrJNtBZdOeDIF+Fng==", "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } } }, "validator": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-11.1.0.tgz", - "integrity": "sha512-qiQ5ktdO7CD6C/5/mYV4jku/7qnqzjrxb3C/Q5wR3vGGinHTgJZN/TdFT3ZX4vXhX2R1PXx42fB1cn5W+uJ4lg==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-12.0.0.tgz", + "integrity": "sha512-r5zA1cQBEOgYlesRmSEwc9LkbfNLTtji+vWyaHzRZUxCTHdsX3bd+sdHfs5tGZ2W6ILGGsxWxCNwT/h3IY/3ng==", "dev": true }, "vary": { @@ -4584,10 +5634,71 @@ "extsprintf": "^1.2.0" } }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "dev": true, + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -4599,14 +5710,11 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "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 }, "wordwrap": { "version": "0.0.3", @@ -4625,31 +5733,29 @@ "strip-ansi": "^5.0.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "color-convert": "^1.9.0" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "color-name": "1.1.3" } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true } } }, @@ -4659,22 +5765,40 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "ws": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz", + "integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==", + "dev": true + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", @@ -4682,15 +5806,15 @@ "dev": true }, "yallist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.0.tgz", - "integrity": "sha512-6gpP93MR+VOOehKbCPchro3wFZNSNmek8A2kbkOAZLIZAYx1KP/zAqwO0sOHi3xJEb+UBz8NaYt/17UNit1Q9w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { "cliui": "^5.0.0", @@ -4702,64 +5826,19 @@ "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "yargs-parser": "^13.1.2" } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - } - }, - "ylru": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz", - "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==", - "dev": true - }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 0a0d8a92..7295288e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "routing-controllers", "private": true, - "version": "0.8.0", + "version": "0.9.0", "description": "Create structured, declarative and beautifully organized class-based controllers with heavy decorators usage for Express / Koa using TypeScript.", "license": "MIT", "readmeFilename": "README.md", @@ -33,58 +33,47 @@ "controllers" ], "dependencies": { - "cookie": "^0.4.0", - "glob": "^7.1.4", - "reflect-metadata": "^0.1.13", - "template-url": "^1.0.0" + "cookie": "0.4.0", + "form-data": "3.0.0", + "glob": "7.1.6", + "multer": "1.4.2", + "reflect-metadata": "0.1.13", + "template-url": "1.0.0" }, "devDependencies": { - "@types/chai": "^4.2.3", - "@types/chai-as-promised": "7.1.2", - "@types/express": "^4.17.1", - "@types/express-session": "1.15.14", - "@types/koa": "^2.0.50", - "@types/mocha": "^5.2.7", - "@types/node": "^12.7.8", - "@types/sinon": "^7.0.13", - "body-parser": "^1.19.0", - "chai": "^4.2.0", - "chai-as-promised": "^7.1.1", - "chakram": "^1.5.0", - "class-transformer": "^0.2.3", - "class-validator": "0.10.1", - "copyfiles": "^2.1.1", - "cors": "^2.8.5", - "cross-env": "6.0.0", - "express": "^4.17.1", - "express-session": "^1.16.2", - "handlebars": "^4.4.0", - "json": "^9.0.6", - "kcors": "^2.2.2", - "koa": "^2.8.2", - "koa-bodyparser": "^4.2.1", - "koa-convert": "^1.2.0", - "koa-multer": "^1.0.2", - "koa-router": "^7.4.0", - "koa-session": "^5.12.3", - "koa-views": "^6.2.1", - "mocha": "^6.2.1", - "multer": "^1.4.2", - "mustache-express": "^1.3.0", - "nyc": "^14.1.1", - "request": "^2.88.0", - "rimraf": "^3.0.0", - "sinon": "^7.5.0", - "sinon-chai": "^3.3.0", - "source-map-support": "^0.5.13", - "ts-node": "^8.4.1", - "tslint": "^5.20.0", - "typedi": "~0.8.0", - "typescript": "~3.6.3" + "@types/body-parser": "1.19.0", + "@types/cors": "2.8.6", + "@types/express": "4.17.4", + "@types/express-session": "1.17.0", + "@types/form-data": "2.5.0", + "@types/jest": "25.1.4", + "@types/multer": "1.4.2", + "@types/mustache-express": "1.2.1", + "@types/node": "13.9.8", + "@types/qs": "6.9.1", + "axios": "0.19.2", + "body-parser": "1.19.0", + "class-transformer": "0.2.3", + "class-validator": "0.11.1", + "copyfiles": "2.2.0", + "cors": "2.8.5", + "express": "4.17.1", + "express-session": "1.17.0", + "handlebars": "4.7.3", + "http-status-codes": "1.4.0", + "jest": "25.2.4", + "json": "9.0.6", + "mustache-express": "1.3.0", + "qs": "6.9.3", + "rimraf": "3.0.2", + "ts-jest": "25.3.0", + "ts-node": "8.8.1", + "typedi": "0.8.0", + "typescript": "3.8.3" }, "peerDependencies": { - "class-transformer": "^0.2.3", - "class-validator": "0.10.1" + "class-transformer": "0.2.3", + "class-validator": "0.11.1" }, "scripts": { "build": "rimraf build && echo Using TypeScript && tsc --version && tsc --pretty", @@ -94,6 +83,6 @@ "package": "npm run build && npm run copy && npm run public && rimraf build/compiled", "pretest": "npm run lint", "public": "json -I -f build/package/package.json -e 'this.private=false'", - "test": "rimraf coverage && cross-env NODE_ENV=test nyc mocha test/**/*.ts" + "test": "rimraf coverage && cross-env jest --coverage" } } diff --git a/sample/sample11-complete-sample-express/modules/blog/middlewares/BlogMiddleware.ts b/sample/sample11-complete-sample-express/modules/blog/middlewares/BlogMiddleware.ts index bb4a78a5..30c2024f 100644 --- a/sample/sample11-complete-sample-express/modules/blog/middlewares/BlogMiddleware.ts +++ b/sample/sample11-complete-sample-express/modules/blog/middlewares/BlogMiddleware.ts @@ -1,11 +1,10 @@ import {ExpressMiddlewareInterface} from "../../../../../src/driver/express/ExpressMiddlewareInterface"; +import express from "express"; export class BlogMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("logging request from blog middleware..."); next("ERROR IN BLOG MIDDLEWARE"); // console.log("extra logging request from blog middleware..."); } - -} \ No newline at end of file +} diff --git a/sample/sample11-complete-sample-express/modules/post/middlewares/PostMiddleware.ts b/sample/sample11-complete-sample-express/modules/post/middlewares/PostMiddleware.ts index 4915be04..49e60f22 100644 --- a/sample/sample11-complete-sample-express/modules/post/middlewares/PostMiddleware.ts +++ b/sample/sample11-complete-sample-express/modules/post/middlewares/PostMiddleware.ts @@ -1,10 +1,9 @@ import {ExpressMiddlewareInterface} from "../../../../../src/driver/express/ExpressMiddlewareInterface"; +import express from "express"; export class PostMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("logging request from post middleware..."); next(); } - -} \ No newline at end of file +} diff --git a/sample/sample11-complete-sample-express/modules/question/middlewares/QuestionMiddleware.ts b/sample/sample11-complete-sample-express/modules/question/middlewares/QuestionMiddleware.ts index d036f47f..d38b5e9e 100644 --- a/sample/sample11-complete-sample-express/modules/question/middlewares/QuestionMiddleware.ts +++ b/sample/sample11-complete-sample-express/modules/question/middlewares/QuestionMiddleware.ts @@ -1,11 +1,9 @@ import {ExpressMiddlewareInterface} from "../../../../../src/driver/express/ExpressMiddlewareInterface"; +import express from "express"; export class QuestionMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("logging request from question middleware..."); next(); - } - -} \ No newline at end of file +} diff --git a/sample/sample6-global-middlewares/CompressionMiddleware.ts b/sample/sample6-global-middlewares/CompressionMiddleware.ts index 2aaa682a..23ece805 100644 --- a/sample/sample6-global-middlewares/CompressionMiddleware.ts +++ b/sample/sample6-global-middlewares/CompressionMiddleware.ts @@ -1,12 +1,11 @@ import {ExpressMiddlewareInterface} from "../../src/driver/express/ExpressMiddlewareInterface"; import {Middleware} from "../../src/decorator/Middleware"; +import express from "express"; @Middleware({ type: "before" }) export class CompressionMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("hello compression ..."); next(); } - -} \ No newline at end of file +} diff --git a/sample/sample6-global-middlewares/EndTimerMiddleware.ts b/sample/sample6-global-middlewares/EndTimerMiddleware.ts index bb1e14f9..266f0014 100644 --- a/sample/sample6-global-middlewares/EndTimerMiddleware.ts +++ b/sample/sample6-global-middlewares/EndTimerMiddleware.ts @@ -1,12 +1,11 @@ import {ExpressMiddlewareInterface} from "../../src/driver/express/ExpressMiddlewareInterface"; import {Middleware} from "../../src/decorator/Middleware"; +import express from "express"; @Middleware({ type: "after" }) export class EndTimerMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("timer is ended."); next(); } - -} \ No newline at end of file +} diff --git a/sample/sample6-global-middlewares/LoggerMiddleware.ts b/sample/sample6-global-middlewares/LoggerMiddleware.ts index 5ca3f32d..5cfc5c71 100644 --- a/sample/sample6-global-middlewares/LoggerMiddleware.ts +++ b/sample/sample6-global-middlewares/LoggerMiddleware.ts @@ -1,12 +1,11 @@ import {ExpressMiddlewareInterface} from "../../src/driver/express/ExpressMiddlewareInterface"; import {Middleware} from "../../src/decorator/Middleware"; +import express from "express"; @Middleware({ type: "before" }) export class LoggerMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("logging request ..."); next(); } - -} \ No newline at end of file +} diff --git a/sample/sample6-global-middlewares/StartTimerMiddleware.ts b/sample/sample6-global-middlewares/StartTimerMiddleware.ts index 3ef07e18..f3e516b5 100644 --- a/sample/sample6-global-middlewares/StartTimerMiddleware.ts +++ b/sample/sample6-global-middlewares/StartTimerMiddleware.ts @@ -1,12 +1,11 @@ import {ExpressMiddlewareInterface} from "../../src/driver/express/ExpressMiddlewareInterface"; import {Middleware} from "../../src/decorator/Middleware"; +import express from "express"; @Middleware({ type: "before" }) export class StartTimerMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("timer is started."); next(); } - -} \ No newline at end of file +} diff --git a/sample/sample9-use-and-middlewares/AllControllerActionsMiddleware.ts b/sample/sample9-use-and-middlewares/AllControllerActionsMiddleware.ts index d86a57d6..93574834 100644 --- a/sample/sample9-use-and-middlewares/AllControllerActionsMiddleware.ts +++ b/sample/sample9-use-and-middlewares/AllControllerActionsMiddleware.ts @@ -1,10 +1,9 @@ import {ExpressMiddlewareInterface} from "../../src/driver/express/ExpressMiddlewareInterface"; +import express from "express"; export class AllControllerActionsMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("controller action run"); next(); } - -} \ No newline at end of file +} diff --git a/sample/sample9-use-and-middlewares/CompressionMiddleware.ts b/sample/sample9-use-and-middlewares/CompressionMiddleware.ts index 8bd1dc6b..f148b571 100644 --- a/sample/sample9-use-and-middlewares/CompressionMiddleware.ts +++ b/sample/sample9-use-and-middlewares/CompressionMiddleware.ts @@ -1,10 +1,9 @@ import {ExpressMiddlewareInterface} from "../../src/driver/express/ExpressMiddlewareInterface"; +import express from "express"; export class CompressionMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { console.log("hello compression ..."); next(); } - -} \ No newline at end of file +} diff --git a/src/ActionParameterHandler.ts b/src/ActionParameterHandler.ts index 41fbdbf1..070a06ce 100644 --- a/src/ActionParameterHandler.ts +++ b/src/ActionParameterHandler.ts @@ -44,8 +44,9 @@ export class ActionParameterHandler { // get parameter value from request and normalize it const value = this.normalizeParamValue(this.driver.getParamFromRequest(action, param), param); - if (isPromiseLike(value)) + if (isPromiseLike(value)) { return value.then(value => this.handleValue(value, action, param)); + } return this.handleValue(value, action, param); } @@ -73,9 +74,11 @@ export class ActionParameterHandler { // check cases when parameter is required but its empty and throw errors in this case if (param.required) { + // TODO: Use lodash.isEmpty()? const isValueEmpty = value === null || value === undefined || value === ""; const isValueEmptyObject = typeof value === "object" && Object.keys(value).length === 0; + // TODO: Change to switch? if (param.type === "body" && !param.name && (isValueEmpty || isValueEmptyObject)) { // body has a special check and error message return Promise.reject(new ParamRequiredError(action, param)); @@ -175,14 +178,14 @@ export class ActionParameterHandler { } else { throw new InvalidParamError(value, parameterName, parameterType); } - + case "date": const parsedDate = new Date(value); if (Number.isNaN(parsedDate.getTime())) { throw new InvalidParamError(value, parameterName, parameterType); } return parsedDate; - + case "string": default: return value; @@ -237,7 +240,7 @@ export class ActionParameterHandler { .catch((validationErrors: ValidationError[]) => { const error: any = new BadRequestError(`Invalid ${paramMetadata.type}, check 'errors' property for more info.`); error.errors = validationErrors; - error.paramName = paramMetadata.name; + error.paramName = paramMetadata.name; throw error; }); } diff --git a/src/RoutingControllers.ts b/src/RoutingControllers.ts index 0fb3cf6b..033f8abc 100644 --- a/src/RoutingControllers.ts +++ b/src/RoutingControllers.ts @@ -6,7 +6,7 @@ import {InterceptorInterface} from "./InterceptorInterface"; import {InterceptorMetadata} from "./metadata/InterceptorMetadata"; import {MetadataBuilder} from "./metadata-builder/MetadataBuilder"; import {RoutingControllersOptions} from "./RoutingControllersOptions"; -import {getFromContainer} from "./container"; +import {getFromContainer} from "./util/container"; import {isPromiseLike} from "./util/isPromiseLike"; import {runInSequence} from "./util/runInSequence"; @@ -124,7 +124,6 @@ export class RoutingControllers { return this.handleCallMethodResult(result, actionMetadata, action, interceptorFns); }).catch(error => { - // otherwise simply handle error without action execution return this.driver.handleError(error, actionMetadata, action); }); diff --git a/src/decorator/Ctx.ts b/src/decorator/Ctx.ts deleted file mode 100644 index e5490fa1..00000000 --- a/src/decorator/Ctx.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {getMetadataArgsStorage} from "../index"; - -/** - * Injects a Koa's Context object to the controller action parameter. - * Must be applied on a controller action parameter. - */ -export function Ctx(): Function { - return function (object: Object, methodName: string, index: number) { - getMetadataArgsStorage().params.push({ - type: "context", - object: object, - method: methodName, - index: index, - parse: false, - required: false - }); - }; -} diff --git a/src/decorator/Session.ts b/src/decorator/Session.ts index d8efaa93..044a0d92 100644 --- a/src/decorator/Session.ts +++ b/src/decorator/Session.ts @@ -6,11 +6,11 @@ import { getMetadataArgsStorage } from "../index"; * Must be applied on a controller action parameter. */ export function Session(options?: ParamOptions): ParameterDecorator { - return function (object: Object, methodName: string, index: number) { + return function (object: Object, methodName: string | symbol, index: number) { getMetadataArgsStorage().params.push({ type: "session", object: object, - method: methodName, + method: methodName as string, index: index, parse: false, // it makes no sense for Session object to be parsed as json required: options && options.required !== undefined ? options.required : true, @@ -18,4 +18,4 @@ export function Session(options?: ParamOptions): ParameterDecorator { validate: options && options.validate !== undefined ? options.validate : false, }); }; -} \ No newline at end of file +} diff --git a/src/decorator/SessionParam.ts b/src/decorator/SessionParam.ts index b6647e1d..bf972a49 100644 --- a/src/decorator/SessionParam.ts +++ b/src/decorator/SessionParam.ts @@ -6,11 +6,11 @@ import { getMetadataArgsStorage } from "../index"; * Must be applied on a controller action parameter. */ export function SessionParam(propertyName: string, options?: ParamOptions): ParameterDecorator { - return function (object: Object, methodName: string, index: number) { + return function (object: Object, methodName: string | symbol, index: number) { getMetadataArgsStorage().params.push({ type: "session-param", object: object, - method: methodName, + method: methodName as string, index: index, name: propertyName, parse: false, // it makes no sense for Session object to be parsed as json @@ -19,4 +19,4 @@ export function SessionParam(propertyName: string, options?: ParamOptions): Para validate: options && options.validate !== undefined ? options.validate : false, }); }; -} \ No newline at end of file +} diff --git a/src/driver/express/ExpressDriver.ts b/src/driver/express/ExpressDriver.ts index c6ea6a6d..9575a15b 100644 --- a/src/driver/express/ExpressDriver.ts +++ b/src/driver/express/ExpressDriver.ts @@ -9,9 +9,11 @@ import {ExpressErrorMiddlewareInterface} from "./ExpressErrorMiddlewareInterface import {AccessDeniedError} from "../../error/AccessDeniedError"; import {AuthorizationCheckerNotDefinedError} from "../../error/AuthorizationCheckerNotDefinedError"; import {isPromiseLike} from "../../util/isPromiseLike"; -import {getFromContainer} from "../../container"; +import {getFromContainer} from "../../util/container"; import {AuthorizationRequiredError} from "../../error/AuthorizationRequiredError"; import {NotFoundError} from "../../index"; +import multer from "multer"; +import bodyParser from "body-parser"; const cookie = require("cookie"); const templateUrl = require("template-url"); @@ -101,9 +103,9 @@ export class ExpressDriver extends BaseDriver { if (actionMetadata.isBodyUsed) { if (actionMetadata.isJsonTyped) { - defaultMiddlewares.push(this.loadBodyParser().json(actionMetadata.bodyExtraOptions)); + defaultMiddlewares.push(bodyParser.json(actionMetadata.bodyExtraOptions)); } else { - defaultMiddlewares.push(this.loadBodyParser().text(actionMetadata.bodyExtraOptions)); + defaultMiddlewares.push(bodyParser.text(actionMetadata.bodyExtraOptions)); } } @@ -138,8 +140,8 @@ export class ExpressDriver extends BaseDriver { }); } + // TODO: handle multer errors gracefully instead of returning HTTP status code 500 if (actionMetadata.isFileUsed || actionMetadata.isFilesUsed) { - const multer = this.loadMulter(); actionMetadata.params .filter(param => param.type === "file") .forEach(param => { @@ -338,7 +340,9 @@ export class ExpressDriver extends BaseDriver { else if (result instanceof Uint8Array) { // check if it's binary data (typed array) options.response.end(Buffer.from(result as any), "binary"); } - else if (result.pipe instanceof Function) { + else if (typeof result.pipe === "function") { + // TODO: instanceof Function is false for pipe() - why?! + // else if (result.pipe instanceof Function) { result.pipe(options.response); } else { // send regular result @@ -380,6 +384,11 @@ export class ExpressDriver extends BaseDriver { response.send(this.processTextError(error)); // todo: no need to do it because express by default does it } } + + // This causes the following test to fail with an ECONNRESET + // @Session(param) should throw required error when param is empty + // See this Github issue: + // https://github.com/typestack/routing-controllers/issues/243 options.next(error); } @@ -438,27 +447,4 @@ export class ExpressDriver extends BaseDriver { throw new Error("Cannot load express. Try to install all required dependencies."); } } - - /** - * Dynamically loads body-parser module. - */ - protected loadBodyParser() { - try { - return require("body-parser"); - } catch (e) { - throw new Error("body-parser package was not found installed. Try to install it: npm install body-parser --save"); - } - } - - /** - * Dynamically loads multer module. - */ - protected loadMulter() { - try { - return require("multer"); - } catch (e) { - throw new Error("multer package was not found installed. Try to install it: npm install multer --save"); - } - } - } diff --git a/src/driver/express/ExpressErrorMiddlewareInterface.ts b/src/driver/express/ExpressErrorMiddlewareInterface.ts index c2cb3192..e4708d2b 100644 --- a/src/driver/express/ExpressErrorMiddlewareInterface.ts +++ b/src/driver/express/ExpressErrorMiddlewareInterface.ts @@ -1,12 +1,13 @@ +import express from "express"; +import {HttpError} from "../../http-error/HttpError"; + /** * Express error middlewares can implement this interface. */ export interface ExpressErrorMiddlewareInterface { - /** * Called before response.send is being called. The data passed to method is the data passed to .send method. * Note that you must return same (or changed) data and it will be passed to .send method. */ - error(error: any, request: any, response: any, next: (err?: any) => any): void; - -} \ No newline at end of file + error(error: HttpError, request: express.Request, response: express.Response, next: express.NextFunction): any; +} diff --git a/src/driver/express/ExpressMiddlewareInterface.ts b/src/driver/express/ExpressMiddlewareInterface.ts index 71dbb054..70bf3016 100644 --- a/src/driver/express/ExpressMiddlewareInterface.ts +++ b/src/driver/express/ExpressMiddlewareInterface.ts @@ -1,13 +1,12 @@ +import express from "express"; /** * Used to register middlewares. * This signature is used for express middlewares. */ export interface ExpressMiddlewareInterface { - /** * Called before controller action is being executed. * This signature is used for Express Middlewares. */ - use(request: any, response: any, next: (err?: any) => any): any; - -} \ No newline at end of file + use(request: express.Request, response: express.Response, next: express.NextFunction): any; +} diff --git a/src/driver/koa/KoaDriver.ts b/src/driver/koa/KoaDriver.ts deleted file mode 100644 index c0d91b93..00000000 --- a/src/driver/koa/KoaDriver.ts +++ /dev/null @@ -1,427 +0,0 @@ -import {Action} from "../../Action"; -import {ActionMetadata} from "../../metadata/ActionMetadata"; -import {BaseDriver} from "../BaseDriver"; -import {MiddlewareMetadata} from "../../metadata/MiddlewareMetadata"; -import {ParamMetadata} from "../../metadata/ParamMetadata"; -import {UseMetadata} from "../../metadata/UseMetadata"; -import {KoaMiddlewareInterface} from "./KoaMiddlewareInterface"; -import {AuthorizationCheckerNotDefinedError} from "../../error/AuthorizationCheckerNotDefinedError"; -import {AccessDeniedError} from "../../error/AccessDeniedError"; -import {isPromiseLike} from "../../util/isPromiseLike"; -import {getFromContainer} from "../../container"; -import {RoleChecker} from "../../RoleChecker"; -import {AuthorizationRequiredError} from "../../error/AuthorizationRequiredError"; -import {HttpError, NotFoundError} from "../../index"; - -const cookie = require("cookie"); -const templateUrl = require("template-url"); - -/** - * Integration with koa framework. - */ -export class KoaDriver extends BaseDriver { - - // ------------------------------------------------------------------------- - // Constructor - // ------------------------------------------------------------------------- - - constructor(public koa?: any, public router?: any) { - super(); - this.loadKoa(); - this.loadRouter(); - this.app = this.koa; - } - - // ------------------------------------------------------------------------- - // Public Methods - // ------------------------------------------------------------------------- - - /** - * Initializes the things driver needs before routes and middleware registration. - */ - initialize() { - const bodyParser = require("koa-bodyparser"); - this.koa.use(bodyParser()); - if (this.cors) { - const cors = require("kcors"); - if (this.cors === true) { - this.koa.use(cors()); - } else { - this.koa.use(cors(this.cors)); - } - } - } - - /** - * Registers middleware that run before controller actions. - */ - registerMiddleware(middleware: MiddlewareMetadata): void { - if ((middleware.instance as KoaMiddlewareInterface).use) { - this.koa.use(function (ctx: any, next: any) { - return (middleware.instance as KoaMiddlewareInterface).use(ctx, next); - }); - } - } - - /** - * Registers action in the driver. - */ - registerAction(actionMetadata: ActionMetadata, executeCallback: (options: Action) => any): void { - - // middlewares required for this action - const defaultMiddlewares: any[] = []; - - if (actionMetadata.isAuthorizedUsed) { - defaultMiddlewares.push((context: any, next: Function) => { - if (!this.authorizationChecker) - throw new AuthorizationCheckerNotDefinedError(); - - const action: Action = { request: context.request, response: context.response, context, next }; - try { - const checkResult = actionMetadata.authorizedRoles instanceof Function ? - getFromContainer(actionMetadata.authorizedRoles).check(action) : - this.authorizationChecker(action, actionMetadata.authorizedRoles); - - const handleError = (result: any) => { - if (!result) { - let error = actionMetadata.authorizedRoles.length === 0 ? new AuthorizationRequiredError(action) : new AccessDeniedError(action); - return this.handleError(error, actionMetadata, action); - } else { - return next(); - } - }; - - if (isPromiseLike(checkResult)) { - return checkResult - .then(result => handleError(result)) - .catch(error => this.handleError(error, actionMetadata, action)); - } else { - return handleError(checkResult); - } - } catch (error) { - return this.handleError(error, actionMetadata, action); - } - }); - } - - if (actionMetadata.isFileUsed || actionMetadata.isFilesUsed) { - const multer = this.loadMulter(); - actionMetadata.params - .filter(param => param.type === "file") - .forEach(param => { - defaultMiddlewares.push(multer(param.extraOptions).single(param.name)); - }); - actionMetadata.params - .filter(param => param.type === "files") - .forEach(param => { - defaultMiddlewares.push(multer(param.extraOptions).array(param.name)); - }); - - defaultMiddlewares.push(this.fixMulterRequestAssignment); - } - - // user used middlewares - const uses = actionMetadata.controllerMetadata.uses.concat(actionMetadata.uses); - const beforeMiddlewares = this.prepareMiddlewares(uses.filter(use => !use.afterAction)); - const afterMiddlewares = this.prepareMiddlewares(uses.filter(use => use.afterAction)); - - // prepare route and route handler function - const route = ActionMetadata.appendBaseRoute(this.routePrefix, actionMetadata.fullRoute); - const routeHandler = (context: any, next: () => Promise) => { - const options: Action = {request: context.request, response: context.response, context, next}; - return executeCallback(options); - }; - - // finally register action in koa - this.router[actionMetadata.type.toLowerCase()](...[ - route, - ...beforeMiddlewares, - ...defaultMiddlewares, - routeHandler, - ...afterMiddlewares - ]); - } - - /** - * Registers all routes in the framework. - */ - registerRoutes() { - this.koa.use(this.router.routes()); - this.koa.use(this.router.allowedMethods()); - } - - /** - * Gets param from the request. - */ - getParamFromRequest(actionOptions: Action, param: ParamMetadata): any { - const context = actionOptions.context; - const request: any = actionOptions.request; - switch (param.type) { - case "body": - return request.body; - - case "body-param": - return request.body[param.name]; - - case "param": - return context.params[param.name]; - - case "params": - return context.params; - - case "session": - return context.session; - - case "session-param": - return context.session[param.name]; - - case "state": - if (param.name) - return context.state[param.name]; - return context.state; - - case "query": - return context.query[param.name]; - - case "queries": - return context.query; - - case "file": - return actionOptions.context.req.file; - - case "files": - return actionOptions.context.req.files; - - case "header": - return context.headers[param.name.toLowerCase()]; - - case "headers": - return request.headers; - - case "cookie": - if (!context.headers.cookie) return; - const cookies = cookie.parse(context.headers.cookie); - return cookies[param.name]; - - case "cookies": - if (!request.headers.cookie) return {}; - return cookie.parse(request.headers.cookie); - } - } - - /** - * Handles result of successfully executed controller action. - */ - handleSuccess(result: any, action: ActionMetadata, options: Action): void { - - // if the action returned the context or the response object itself, short-circuits - if (result && (result === options.response || result === options.context)) { - return options.next(); - } - - // transform result if needed - result = this.transformResult(result, action, options); - - if (action.redirect) { // if redirect is set then do it - if (typeof result === "string") { - options.response.redirect(result); - } else if (result instanceof Object) { - options.response.redirect(templateUrl(action.redirect, result)); - } else { - options.response.redirect(action.redirect); - } - } else if (action.renderedTemplate) { // if template is set then render it // TODO: not working in koa - const renderOptions = result && result instanceof Object ? result : {}; - - this.koa.use(async function (ctx: any, next: any) { - await ctx.render(action.renderedTemplate, renderOptions); - }); - } - else if (result === undefined) { // throw NotFoundError on undefined response - if (action.undefinedResultCode instanceof Function) { - throw new (action.undefinedResultCode as any)(options); - - } else if (!action.undefinedResultCode) { - throw new NotFoundError(); - } - } - else if (result === null) { // send null response - if (action.nullResultCode instanceof Function) - throw new (action.nullResultCode as any)(options); - - options.response.body = null; - } - else if (result instanceof Uint8Array) { // check if it's binary data (typed array) - options.response.body = Buffer.from(result as any); - } - else { // send regular result - if (result instanceof Object) { - options.response.body = result; - } else { - options.response.body = result; - } - } - - // set http status code - if (result === undefined && action.undefinedResultCode) { - options.response.status = action.undefinedResultCode; - } - else if (result === null && action.nullResultCode) { - options.response.status = action.nullResultCode; - - } else if (action.successHttpCode) { - options.response.status = action.successHttpCode; - - } else if (options.response.body === null) { - options.response.status = 204; - } - - // apply http headers - Object.keys(action.headers).forEach(name => { - options.response.set(name, action.headers[name]); - }); - - return options.next(); - } - - /** - * Handles result of failed executed controller action. - */ - handleError(error: any, action: ActionMetadata | undefined, options: Action) { - return new Promise((resolve, reject) => { - if (this.isDefaultErrorHandlingEnabled) { - - // apply http headers - if (action) { - Object.keys(action.headers).forEach(name => { - options.response.set(name, action.headers[name]); - }); - } - - // send error content - if (action && action.isJsonTyped) { - options.response.body = this.processJsonError(error); - } else { - options.response.body = this.processTextError(error); - } - - // set http status - if (error instanceof HttpError && error.httpCode) { - options.response.status = error.httpCode; - } else { - options.response.status = 500; - } - - return resolve(); - } - return reject(error); - }); - } - - // ------------------------------------------------------------------------- - // Protected Methods - // ------------------------------------------------------------------------- - - /** - * Creates middlewares from the given "use"-s. - */ - protected prepareMiddlewares(uses: UseMetadata[]) { - const middlewareFunctions: Function[] = []; - uses.forEach(use => { - if (use.middleware.prototype && use.middleware.prototype.use) { // if this is function instance of MiddlewareInterface - middlewareFunctions.push((context: any, next: (err?: any) => Promise) => { - try { - const useResult = (getFromContainer(use.middleware) as KoaMiddlewareInterface).use(context, next); - if (isPromiseLike(useResult)) { - useResult.catch((error: any) => { - this.handleError(error, undefined, { - request: context.req, - response: context.res, - context, - next - }); - return error; - }); - } - - return useResult; - } catch (error) { - this.handleError(error, undefined, { - request: context.request, - response: context.response, - context, - next - }); - } - }); - - } else { - middlewareFunctions.push(use.middleware); - } - }); - return middlewareFunctions; - } - - /** - * Dynamically loads koa and required koa-router module. - */ - protected loadKoa() { - if (require) { - if (!this.koa) { - try { - this.koa = new (require("koa"))(); - } catch (e) { - throw new Error("koa package was not found installed. Try to install it: npm install koa@next --save"); - } - } - } else { - throw new Error("Cannot load koa. Try to install all required dependencies."); - } - } - - /** - * Dynamically loads koa-router module. - */ - private loadRouter() { - if (require) { - if (!this.router) { - try { - this.router = new (require("koa-router"))(); - } catch (e) { - throw new Error("koa-router package was not found installed. Try to install it: npm install koa-router@next --save"); - } - } - } else { - throw new Error("Cannot load koa. Try to install all required dependencies."); - } - } - - /** - * Dynamically loads koa-multer module. - */ - private loadMulter() { - try { - return require("koa-multer"); - } catch (e) { - throw new Error("koa-multer package was not found installed. Try to install it: npm install koa-multer --save"); - } - } - - /** - * This middleware fixes a bug on koa-multer implementation. - * - * This bug should be fixed by koa-multer PR #15: https://github.com/koa-modules/multer/pull/15 - */ - private async fixMulterRequestAssignment(ctx: any, next: Function) { - if ("request" in ctx) { - if (ctx.req.body) ctx.request.body = ctx.req.body; - if (ctx.req.file) ctx.request.file = ctx.req.file; - if (ctx.req.files) { - ctx.request.files = ctx.req.files; - ctx.files = ctx.req.files; - } - } - - return await next(); - } -} \ No newline at end of file diff --git a/src/driver/koa/KoaMiddlewareInterface.ts b/src/driver/koa/KoaMiddlewareInterface.ts deleted file mode 100644 index 50c088d6..00000000 --- a/src/driver/koa/KoaMiddlewareInterface.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Used to register middlewares. - * This signature is used for koa middlewares. - */ -export interface KoaMiddlewareInterface { - - /** - * Called before controller action is being executed. - */ - use(context: any, next: (err?: any) => Promise): Promise; - -} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index e143ab93..13ce4c3c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,19 +1,17 @@ import {CustomParameterDecorator} from "./CustomParameterDecorator"; import {BaseDriver} from "./driver/BaseDriver"; import {ExpressDriver} from "./driver/express/ExpressDriver"; -import {KoaDriver} from "./driver/koa/KoaDriver"; import {MetadataArgsStorage} from "./metadata-builder/MetadataArgsStorage"; import {RoutingControllers} from "./RoutingControllers"; import {RoutingControllersOptions} from "./RoutingControllersOptions"; import {ValidationOptions} from "class-validator"; import {importClassesFromDirectories} from "./util/importClassesFromDirectories"; +import {Application as ExpressApplication} from "express"; // ------------------------------------------------------------------------- // Main exports // ------------------------------------------------------------------------- - -export * from "./container"; - +export * from "./util/container"; export * from "./decorator/Authorized"; export * from "./decorator/Body"; export * from "./decorator/BodyParam"; @@ -21,7 +19,6 @@ export * from "./decorator/ContentType"; export * from "./decorator/Controller"; export * from "./decorator/CookieParam"; export * from "./decorator/CookieParams"; -export * from "./decorator/Ctx"; export * from "./decorator/CurrentUser"; export * from "./decorator/Delete"; export * from "./decorator/Get"; @@ -72,7 +69,6 @@ export * from "./http-error/UnauthorizedError"; export * from "./driver/express/ExpressMiddlewareInterface"; export * from "./driver/express/ExpressErrorMiddlewareInterface"; -export * from "./driver/koa/KoaMiddlewareInterface"; export * from "./metadata-builder/MetadataArgsStorage"; export * from "./metadata/ActionMetadata"; export * from "./metadata/ControllerMetadata"; @@ -90,7 +86,6 @@ export * from "./InterceptorInterface"; export * from "./driver/BaseDriver"; export * from "./driver/express/ExpressDriver"; -export * from "./driver/koa/KoaDriver"; // ------------------------------------------------------------------------- // Main Functions @@ -118,27 +113,13 @@ export function useExpressServer(expressApp: T, options?: RoutingControllersO /** * Registers all loaded actions in your express application. */ -export function createExpressServer(options?: RoutingControllersOptions): any { +// TODO: Rename me to createExpressApplication +// express is an application, express.listen returns an http/https server +export function createExpressServer(options?: RoutingControllersOptions): ExpressApplication { const driver = new ExpressDriver(); return createServer(driver, options); } -/** - * Registers all loaded actions in your koa application. - */ -export function useKoaServer(koaApp: T, options?: RoutingControllersOptions): T { - const driver = new KoaDriver(koaApp); - return createServer(driver, options); -} - -/** - * Registers all loaded actions in your koa application. - */ -export function createKoaServer(options?: RoutingControllersOptions): any { - const driver = new KoaDriver(); - return createServer(driver, options); -} - /** * Registers all loaded actions in your application using selected driver. */ diff --git a/src/metadata/ControllerMetadata.ts b/src/metadata/ControllerMetadata.ts index 3ecd2b3a..d04b1fde 100644 --- a/src/metadata/ControllerMetadata.ts +++ b/src/metadata/ControllerMetadata.ts @@ -1,7 +1,7 @@ import {ActionMetadata} from "./ActionMetadata"; import {ControllerMetadataArgs} from "./args/ControllerMetadataArgs"; import {UseMetadata} from "./UseMetadata"; -import {getFromContainer} from "../container"; +import {getFromContainer} from "../util/container"; import {ResponseHandlerMetadata} from "./ResponseHandleMetadata"; import {InterceptorMetadata} from "./InterceptorMetadata"; @@ -57,7 +57,7 @@ export class ControllerMetadata { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- - + constructor(args: ControllerMetadataArgs) { this.target = args.target; this.route = args.route; @@ -89,4 +89,4 @@ export class ControllerMetadata { this.authorizedRoles = [].concat((authorizedHandler && authorizedHandler.value) || []); } -} \ No newline at end of file +} diff --git a/src/metadata/MiddlewareMetadata.ts b/src/metadata/MiddlewareMetadata.ts index d7818a38..08a140f3 100644 --- a/src/metadata/MiddlewareMetadata.ts +++ b/src/metadata/MiddlewareMetadata.ts @@ -1,8 +1,7 @@ import {MiddlewareMetadataArgs} from "./args/MiddlewareMetadataArgs"; import {ExpressMiddlewareInterface} from "../driver/express/ExpressMiddlewareInterface"; import {ExpressErrorMiddlewareInterface} from "../driver/express/ExpressErrorMiddlewareInterface"; -import {getFromContainer} from "../container"; -import {KoaMiddlewareInterface} from "../driver/koa/KoaMiddlewareInterface"; +import {getFromContainer} from "../util/container"; /** * Middleware metadata. @@ -27,7 +26,7 @@ export class MiddlewareMetadata { * Execution priority of the middleware. */ priority: number; - + /** * Indicates if middleware must be executed after routing action is executed. */ @@ -36,7 +35,7 @@ export class MiddlewareMetadata { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- - + constructor(args: MiddlewareMetadataArgs) { this.global = args.global; this.target = args.target; @@ -51,8 +50,7 @@ export class MiddlewareMetadata { /** * Gets middleware instance from the container. */ - get instance(): ExpressMiddlewareInterface|KoaMiddlewareInterface|ExpressErrorMiddlewareInterface { - return getFromContainer(this.target); + get instance(): ExpressMiddlewareInterface | ExpressErrorMiddlewareInterface { + return getFromContainer(this.target); } - -} \ No newline at end of file +} diff --git a/src/container.ts b/src/util/container.ts similarity index 100% rename from src/container.ts rename to src/util/container.ts diff --git a/test/fakes/global-options/FakeService.ts b/test/fakes/global-options/FakeService.ts index 3a4925d3..d0535356 100644 --- a/test/fakes/global-options/FakeService.ts +++ b/test/fakes/global-options/FakeService.ts @@ -1,5 +1,4 @@ export class FakeService { - fileMiddlewareCalled = false; videoMiddlewareCalled = false; questionMiddlewareCalled = false; @@ -8,27 +7,22 @@ export class FakeService { fileMiddleware() { this.fileMiddlewareCalled = true; - console.log("fake service!"); } videoMiddleware() { this.videoMiddlewareCalled = true; - console.log("fake service!"); } questionMiddleware() { this.questionMiddlewareCalled = true; - console.log("fake service!"); } questionErrorMiddleware() { this.questionErrorMiddlewareCalled = true; - console.log("fake service!"); } postMiddleware() { this.postMiddlewareCalled = true; - console.log("fake service!"); } reset() { @@ -38,7 +32,6 @@ export class FakeService { this.questionErrorMiddlewareCalled = false; this.postMiddlewareCalled = false; } - } -export const defaultFakeService = new FakeService(); \ No newline at end of file +export const defaultFakeService = new FakeService(); diff --git a/test/fakes/global-options/SessionMiddleware.ts b/test/fakes/global-options/SessionMiddleware.ts index 427a1894..ee262b83 100644 --- a/test/fakes/global-options/SessionMiddleware.ts +++ b/test/fakes/global-options/SessionMiddleware.ts @@ -1,26 +1,26 @@ import {ExpressMiddlewareInterface} from "../../../src/driver/express/ExpressMiddlewareInterface"; -import * as session from "express-session"; - -const convert = require("koa-convert"); -const KoaSession = require("koa-session"); +import session from "express-session"; +import express from "express"; export class SessionMiddleware implements ExpressMiddlewareInterface { - public use (requestOrContext: any, responseOrNext: any, next?: (err?: any) => any): any { - if (next) { - return this.expSession(requestOrContext, responseOrNext, next); - } else { - if (!this.koaSession) { - this.koaSession = convert(KoaSession(requestOrContext.app)); - } - return this.koaSession(requestOrContext, responseOrNext); - } - } - - private expSession = session({ - secret: "19majkel94_helps_pleerock", + private static instance: SessionMiddleware; + private requestHandler: express.RequestHandler = session({ + secret: "super-secret", resave: false, - saveUninitialized: true, + saveUninitialized: true }); - private koaSession: any; -} \ No newline at end of file + constructor() { + if (SessionMiddleware.instance) { + return SessionMiddleware.instance + } + + SessionMiddleware.instance = this; + } + + public use(request: express.Request, response: express.Response, next?: express.NextFunction): express.RequestHandler { + if (next) { + return this.requestHandler(request, response, next); + } + } +} diff --git a/test/fakes/global-options/express-middlewares/post/PostMiddleware.ts b/test/fakes/global-options/express-middlewares/post/PostMiddleware.ts index 033572ce..e7d4f77a 100644 --- a/test/fakes/global-options/express-middlewares/post/PostMiddleware.ts +++ b/test/fakes/global-options/express-middlewares/post/PostMiddleware.ts @@ -1,13 +1,12 @@ import {ExpressMiddlewareInterface} from "../../../../../src/driver/express/ExpressMiddlewareInterface"; import {defaultFakeService} from "../../FakeService"; import {Middleware} from "../../../../../src/decorator/Middleware"; +import express from "express"; @Middleware({ type: "before" }) export class PostMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: (err?: any) => any): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { defaultFakeService.postMiddleware(); next(); } - -} \ No newline at end of file +} diff --git a/test/fakes/global-options/express-middlewares/question/QuestionMiddleware.ts b/test/fakes/global-options/express-middlewares/question/QuestionMiddleware.ts index 389a6735..b4e915fa 100644 --- a/test/fakes/global-options/express-middlewares/question/QuestionMiddleware.ts +++ b/test/fakes/global-options/express-middlewares/question/QuestionMiddleware.ts @@ -1,13 +1,12 @@ import {ExpressMiddlewareInterface} from "../../../../../src/driver/express/ExpressMiddlewareInterface"; import {defaultFakeService} from "../../FakeService"; import {Middleware} from "../../../../../src/decorator/Middleware"; +import express from "express"; @Middleware({ type: "before" }) export class QuestionMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: (err?: any) => any): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { defaultFakeService.questionMiddleware(); return next(); } - -} \ No newline at end of file +} diff --git a/test/fakes/global-options/koa-middlewares/FileMiddleware.ts b/test/fakes/global-options/koa-middlewares/FileMiddleware.ts deleted file mode 100644 index 01fb61b7..00000000 --- a/test/fakes/global-options/koa-middlewares/FileMiddleware.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {ExpressMiddlewareInterface} from "../../../../src/driver/express/ExpressMiddlewareInterface"; -import {defaultFakeService} from "../FakeService"; -import {Middleware} from "../../../../src/decorator/Middleware"; - -@Middleware({ type: "before" }) -export class FileMiddleware implements ExpressMiddlewareInterface { - - use(context: any, next?: (err?: any) => Promise): Promise { - defaultFakeService.fileMiddleware(); - return next(); - } - -} \ No newline at end of file diff --git a/test/fakes/global-options/koa-middlewares/SetStateMiddleware.ts b/test/fakes/global-options/koa-middlewares/SetStateMiddleware.ts deleted file mode 100644 index 39bdaddd..00000000 --- a/test/fakes/global-options/koa-middlewares/SetStateMiddleware.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {ExpressMiddlewareInterface} from "../../../../src/driver/express/ExpressMiddlewareInterface"; -import {User} from "../User"; - -export class SetStateMiddleware implements ExpressMiddlewareInterface { - public use (context: any, next: (err?: any) => Promise): Promise { - const user = new User(); - user.username = "pleerock"; - user.location = "Dushanbe, Tajikistan"; - user.twitter = "https://twitter.com/pleerock"; - context.state = user; - return next(); - } -} \ No newline at end of file diff --git a/test/fakes/global-options/koa-middlewares/VideoMiddleware.ts b/test/fakes/global-options/koa-middlewares/VideoMiddleware.ts deleted file mode 100644 index 90f59c42..00000000 --- a/test/fakes/global-options/koa-middlewares/VideoMiddleware.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {ExpressMiddlewareInterface} from "../../../../src/driver/express/ExpressMiddlewareInterface"; -import {defaultFakeService} from "../FakeService"; -import {Middleware} from "../../../../src/decorator/Middleware"; - -@Middleware({ type: "before" }) -export class VideoMiddleware implements ExpressMiddlewareInterface { - - use(context: any, next?: (err?: any) => Promise): Promise { - defaultFakeService.videoMiddleware(); - return next(); - } - -} \ No newline at end of file diff --git a/test/functional/action-params.spec.ts b/test/functional/action-params.spec.ts index 5028a381..2e221a65 100644 --- a/test/functional/action-params.spec.ts +++ b/test/functional/action-params.spec.ts @@ -1,12 +1,11 @@ import "reflect-metadata"; - +import fs from "fs"; +import qs from "qs"; +import FormData from "form-data"; import {IsString, IsBoolean, Min, MaxLength, ValidateNested} from "class-validator"; -import {getMetadataArgsStorage, createExpressServer, createKoaServer} from "../../src/index"; -import {assertRequest} from "./test-utils"; -import {User} from "../fakes/global-options/User"; +import {getMetadataArgsStorage, createExpressServer} from "../../src/index"; import {Controller} from "../../src/decorator/Controller"; import {Get} from "../../src/decorator/Get"; -import {Ctx} from "../../src/decorator/Ctx"; import {Req} from "../../src/decorator/Req"; import {Res} from "../../src/decorator/Res"; import {Param} from "../../src/decorator/Param"; @@ -14,7 +13,6 @@ import {Post} from "../../src/decorator/Post"; import {UseBefore} from "../../src/decorator/UseBefore"; import {Session} from "../../src/decorator/Session"; import {SessionParam} from "../../src/decorator/SessionParam"; -import {State} from "../../src/decorator/State"; import {QueryParam} from "../../src/decorator/QueryParam"; import {QueryParams} from "../../src/decorator/QueryParams"; import {HeaderParam} from "../../src/decorator/HeaderParam"; @@ -23,1005 +21,921 @@ import {Body} from "../../src/decorator/Body"; import {BodyParam} from "../../src/decorator/BodyParam"; import {UploadedFile} from "../../src/decorator/UploadedFile"; import {UploadedFiles} from "../../src/decorator/UploadedFiles"; -import {ContentType} from "../../src/decorator/ContentType"; import {JsonController} from "../../src/decorator/JsonController"; - -const chakram = require("chakram"); -const expect = chakram.expect; - -describe("action parameters", () => { - - let paramUserId: number, paramFirstId: number, paramSecondId: number; - let sessionTestElement: string; - let queryParamSortBy: string, queryParamCount: string, queryParamLimit: number, queryParamShowAll: boolean, queryParamFilter: any; - let queryParams1: {[key: string]: any}, queryParams2: {[key: string]: any}, queryParams3: {[key: string]: any}; - let headerParamToken: string, headerParamCount: number, headerParamLimit: number, headerParamShowAll: boolean, headerParamFilter: any; - let cookieParamToken: string, cookieParamCount: number, cookieParamLimit: number, cookieParamShowAll: boolean, cookieParamFilter: any; - let body: string; - let bodyParamName: string, bodyParamAge: number, bodyParamIsActive: boolean; - let uploadedFileName: string; - let uploadedFilesFirstName: string; - let uploadedFilesSecondName: string; - let requestReq: any, requestRes: any; - - beforeEach(() => { - paramUserId = undefined; - paramFirstId = undefined; - paramSecondId = undefined; - sessionTestElement = undefined; - queryParamSortBy = undefined; - queryParamCount = undefined; - queryParamLimit = undefined; - queryParamShowAll = undefined; - queryParamFilter = undefined; - queryParams1 = undefined; - queryParams2 = undefined; - queryParams3 = undefined; - headerParamToken = undefined; - headerParamCount = undefined; - headerParamShowAll = undefined; - headerParamLimit = undefined; - headerParamFilter = undefined; - cookieParamToken = undefined; - cookieParamCount = undefined; - cookieParamShowAll = undefined; - cookieParamLimit = undefined; - cookieParamFilter = undefined; - body = undefined; - bodyParamName = undefined; - bodyParamAge = undefined; - bodyParamIsActive = undefined; - uploadedFileName = undefined; - uploadedFilesFirstName = undefined; - uploadedFilesSecondName = undefined; - requestReq = undefined; - requestRes = undefined; - }); - - before(() => { - // reset metadata args storage - getMetadataArgsStorage().reset(); - - const {SetStateMiddleware} = require("../fakes/global-options/koa-middlewares/SetStateMiddleware"); - const {SessionMiddleware} = require("../fakes/global-options/SessionMiddleware"); - - class NestedQueryClass { - @Min(5) - num: number; - - @IsString() - str: string; - - @IsBoolean() - isFive: boolean; +import {AxiosError, AxiosInstance, AxiosResponse} from "axios"; +import HttpStatusCodes from "http-status-codes"; +import {SessionMiddleware} from "../fakes/global-options/SessionMiddleware"; +import bodyParser from "body-parser"; +import DoneCallback = jest.DoneCallback; +import path from "path"; +import {Server as HttpServer, ServerResponse} from "http"; +import express from "express"; +import {axios} from "../utilities/axios"; + +let expressServer: HttpServer; +let paramUserId: number | undefined, paramFirstId: number | undefined, paramSecondId: number | undefined; +let sessionTestElement: string | undefined; +let queryParamSortBy: string | undefined, queryParamCount: string | undefined, queryParamLimit: number | undefined, + queryParamShowAll: boolean | undefined, queryParamFilter: Object | undefined; +let queryParams1: { [key: string]: any } | undefined, queryParams2: { [key: string]: any } | undefined, + queryParams3: { [key: string]: any } | undefined; +let headerParamToken: string | undefined, headerParamCount: number | undefined, headerParamLimit: number | undefined, + headerParamShowAll: boolean | undefined, headerParamFilter: Object | undefined; +let cookieParamToken: string | undefined, cookieParamCount: number | undefined, cookieParamLimit: number | undefined, + cookieParamShowAll: boolean | undefined, cookieParamFilter: Object | undefined; +let body: string | undefined; +let bodyParamName: string | undefined, bodyParamAge: number | undefined, bodyParamIsActive: boolean | undefined; +let expressRequest: express.Request | undefined, expressResponse: express.Response | undefined; +const urlencodedParser: any = bodyParser.urlencoded({ extended: true }); + +beforeEach(() => { + paramUserId = undefined; + paramFirstId = undefined; + paramSecondId = undefined; + sessionTestElement = undefined; + queryParamSortBy = undefined; + queryParamCount = undefined; + queryParamLimit = undefined; + queryParamShowAll = undefined; + queryParamFilter = undefined; + queryParams1 = undefined; + queryParams2 = undefined; + queryParams3 = undefined; + headerParamToken = undefined; + headerParamCount = undefined; + headerParamShowAll = undefined; + headerParamLimit = undefined; + headerParamFilter = undefined; + cookieParamToken = undefined; + cookieParamCount = undefined; + cookieParamShowAll = undefined; + cookieParamLimit = undefined; + cookieParamFilter = undefined; + body = undefined; + bodyParamName = undefined; + bodyParamAge = undefined; + bodyParamIsActive = undefined; + expressRequest = undefined; + expressResponse = undefined; +}); + +beforeAll((done) => { + getMetadataArgsStorage().reset(); + + class NestedQueryClass { + @Min(5) + num: number; + + @IsString() + str: string; + + @IsBoolean() + isFive: boolean; + } + + class QueryClass { + @MaxLength(5) + sortBy?: string; + + @IsString() + count?: string; + + @Min(5) + limit?: number; + + @IsBoolean() + showAll: boolean = true; + + @ValidateNested() + myObject: NestedQueryClass; + } + + @Controller() + class UserActionParamsController { + @Get("/users") + getUsers(@Req() request: express.Request, @Res() response: express.Response): string { + expressRequest = request; + expressResponse = response; + return "hello"; } - class QueryClass { - @MaxLength(5) - sortBy?: string; - - @IsString() - count?: string; - - @Min(5) - limit?: number; - - @IsBoolean() - showAll: boolean = true; - - @ValidateNested() - myObject: NestedQueryClass; + @Get("/users-direct") + getUsersDirect(@Res() response: express.Response): express.Response { + return response.status(201).contentType("custom/x-sample").send("hi, I was written directly to the response"); } - @Controller() - class UserActionParamsController { - - @Get("/users") - getUsers(@Req() request: any, @Res() response: any): any { - requestReq = request; - requestRes = response; - return "hello"; - } - - @Get("/users-direct") - getUsersDirect(@Res() response: any): any { - if (typeof response.send === "function") - return response.status(201).contentType("custom/x-sample").send("hi, I was written directly to the response"); - else { - response.status = 201; - response.type = "custom/x-sample; charset=utf-8"; - response.body = "hi, I was written directly to the response"; - return response; - } - } - - @Get("/users-direct/ctx") - getUsersDirectKoa(@Ctx() ctx: any): any { - ctx.response.status = 201; - ctx.response.type = "custom/x-sample; charset=utf-8"; - ctx.response.body = "hi, I was written directly to the response using Koa Ctx"; - return ctx; - } - - @Get("/users/:userId") - getUser(@Param("userId") userId: number) { - paramUserId = userId; - return `${userId}`; - } - - @Get("/users/:firstId/photos/:secondId") - getUserPhoto(@Param("firstId") firstId: number, - @Param("secondId") secondId: number) { - paramFirstId = firstId; - paramSecondId = secondId; - return `${firstId},${secondId}`; - } - - @Post("/session/") - @UseBefore(SessionMiddleware) - addToSession(@Session() session: any) { - session["testElement"] = "@Session test"; - session["fakeObject"] = { - name: "fake", - fake: true, - value: 666 - }; - return `@Session`; - } - - @Get("/session/") - @UseBefore(SessionMiddleware) - loadFromSession(@SessionParam("testElement") testElement: string) { - sessionTestElement = testElement; - return `${testElement}`; - } - - @Get("/not-use-session/") - notUseSession(@SessionParam("testElement") testElement: string) { - sessionTestElement = testElement; - return `${testElement}`; - } - - @Get("/session-param-empty/") - @UseBefore(SessionMiddleware) - loadEmptyParamFromSession(@SessionParam("empty", { required: false }) emptyElement: string) { - sessionTestElement = emptyElement; - return `${emptyElement === undefined}`; - } - - @Get("/session-param-empty-error/") - @UseBefore(SessionMiddleware) - errorOnLoadEmptyParamFromSession(@SessionParam("empty") emptyElement: string) { - sessionTestElement = emptyElement; - return `${emptyElement === undefined}`; - } - - @Get("/state") - @UseBefore(SetStateMiddleware) - @ContentType("application/json") - getState(@State() state: User) { - return state; - } - - @Get("/state/username") - @UseBefore(SetStateMiddleware) - getUsernameFromState(@State("username") username: string) { - return `${username}`; - } - - @Get("/photos") - getPhotos(@QueryParam("sortBy") sortBy: string, - @QueryParam("count") count: string, - @QueryParam("limit") limit: number, - @QueryParam("showAll") showAll: boolean) { - queryParamSortBy = sortBy; - queryParamCount = count; - queryParamLimit = limit; - queryParamShowAll = showAll; - return `hello`; - } - - @Get("/photos-params") - getPhotosWithQuery(@QueryParams() query: QueryClass) { - queryParams1 = query; - return `hello`; - } - - @Get("/photos-params-no-validate") - getPhotosWithQueryAndNoValidation(@QueryParams({ validate: false }) query: QueryClass) { - queryParams2 = query; - return `hello`; - } - - @Get("/photos-params-optional") - getPhotosWithOptionalQuery(@QueryParams({ validate: { skipMissingProperties: true } }) query: QueryClass) { - queryParams3 = query; - return `hello`; - } + @Get("/users/:userId") + getUser(@Param("userId") userId: number): string { + paramUserId = userId; + return `${userId}`; + } - @Get("/photos-with-required") - getPhotosWithIdRequired(@QueryParam("limit", { required: true }) limit: number) { - queryParamLimit = limit; - return `${limit}`; - } + @Get("/users/:firstId/photos/:secondId") + getUserPhoto(@Param("firstId") firstId: number, + @Param("secondId") secondId: number): string { + paramFirstId = firstId; + paramSecondId = secondId; + return `${firstId},${secondId}`; + } - @Get("/photos-with-json") - getPhotosWithJsonParam(@QueryParam("filter", { parse: true }) filter: { keyword: string, limit: number }) { - queryParamFilter = filter; - return `hello`; - } + @Post("/session/") + @UseBefore(SessionMiddleware) + addToSession(@Session() session: any): string { + session["testElement"] = "@Session test"; + session["fakeObject"] = { + name: "fake", + fake: true, + value: 666 + }; + return "@Session"; + } - @Get("/posts") - getPosts(@HeaderParam("token") token: string, - @HeaderParam("count") count: number, - @HeaderParam("showAll") showAll: boolean) { - headerParamToken = token; - headerParamCount = count; - headerParamShowAll = showAll; - return `hello`; - } + @Get("/session/") + @UseBefore(SessionMiddleware) + loadFromSession(@SessionParam("testElement") testElement: string): string { + sessionTestElement = testElement; + return `${testElement}`; + } - @Get("/posts-with-required") - getPostsWithIdRequired(@HeaderParam("limit", { required: true }) limit: number) { - headerParamLimit = limit; - return `${limit}`; - } + @Get("/not-use-session/") + notUseSession(@SessionParam("testElement") testElement: string): string { + sessionTestElement = testElement; + return `${testElement}`; + } - @Get("/posts-with-json") - getPostsWithJsonParam(@HeaderParam("filter", { parse: true }) filter: { keyword: string, limit: number }) { - headerParamFilter = filter; - return `hello`; - } + @Get("/session-param-empty/") + @UseBefore(SessionMiddleware) + loadEmptyParamFromSession(@SessionParam("empty", {required: false}) emptyElement: string): string { + sessionTestElement = emptyElement; + return `${emptyElement === undefined}`; + } - @Get("/questions") - getQuestions(@CookieParam("token") token: string, - @CookieParam("count") count: number, - @CookieParam("showAll") showAll: boolean) { - cookieParamToken = token; - cookieParamCount = count; - cookieParamShowAll = showAll; - return `hello`; - } + @Get("/session-param-empty-error/") + @UseBefore(SessionMiddleware) + errorOnLoadEmptyParamFromSession(@SessionParam("empty", {required: true}) emptyElement: string): string { + sessionTestElement = emptyElement; + return `${emptyElement === undefined}`; + } - @Get("/questions-with-required") - getQuestionsWithIdRequired(@CookieParam("limit", { required: true }) limit: number) { - cookieParamLimit = limit; - return `hello`; - } + @Get("/photos") + getPhotos(@QueryParam("sortBy") sortBy: string, + @QueryParam("count") count: string, + @QueryParam("limit") limit: number, + @QueryParam("showAll") showAll: boolean): string { + queryParamSortBy = sortBy; + queryParamCount = count; + queryParamLimit = limit; + queryParamShowAll = showAll; + return `hello`; + } - @Get("/questions-with-json") - getQuestionsWithJsonParam(@CookieParam("filter", { parse: true }) filter: { keyword: string, limit: number }) { - cookieParamFilter = filter; - return `hello`; - } + @Get("/photos-params") + getPhotosWithQuery(@QueryParams() query: QueryClass): string { + queryParams1 = query; + return `hello`; + } - @Post("/questions") - postQuestion(@Body() question: string) { - body = question; - return `hello`; - } + @Get("/photos-params-no-validate") + getPhotosWithQueryAndNoValidation(@QueryParams({validate: false}) query: QueryClass): string { + queryParams2 = query; + return `hello`; + } - @Post("/questions-with-required") - postRequiredQuestion(@Body({ required: true }) question: string) { - body = question; - return `hello`; - } + @Get("/photos-params-optional") + getPhotosWithOptionalQuery(@QueryParams({validate: {skipMissingProperties: true}}) query: QueryClass): string { + queryParams3 = query; + return `hello`; + } - @Post("/files") - postFile(@UploadedFile("myfile") file: any): any { - uploadedFileName = file.originalname; - return `${uploadedFileName}`; - } + @Get("/photos-with-required") + getPhotosWithIdRequired(@QueryParam("limit", {required: true}) limit: number): string { + queryParamLimit = limit; + return `${limit}`; + } - @Post("/files-with-body") - postFileWithBody(@UploadedFile("myfile") file: any, @Body() body: any): any { - uploadedFileName = file.originalname; - return `${uploadedFileName} - ${JSON.stringify(body)}`; - } + @Get("/photos-with-json") + getPhotosWithJsonParam(@QueryParam("filter", {parse: true}) filter: { keyword: string, limit: number }): string { + queryParamFilter = filter; + return `hello`; + } - @Post("/files-with-body-param") - postFileWithBodyParam(@UploadedFile("myfile") file: any, @BodyParam("p1") p1: string): any { - uploadedFileName = file.originalname; - return `${uploadedFileName} - ${p1}`; - } + @Get("/posts") + getPosts(@HeaderParam("token") token: string, + @HeaderParam("count") count: number, + @HeaderParam("showAll") showAll: boolean): string { + headerParamToken = token; + headerParamCount = count; + headerParamShowAll = showAll; + return `hello`; + } - @Post("/files-with-limit") - postFileWithLimit(@UploadedFile("myfile", { options: { limits: { fileSize: 2 } } }) file: any): any { - return `${file.originalname}`; - } + @Get("/posts-with-required") + getPostsWithIdRequired(@HeaderParam("limit", {required: true}) limit: number): string { + headerParamLimit = limit; + return `${limit}`; + } - @Post("/files-with-required") - postFileWithRequired(@UploadedFile("myfile", { required: true }) file: any): any { - return `${file.originalname}`; - } + @Get("/posts-with-json") + getPostsWithJsonParam(@HeaderParam("filter", {parse: true}) filter: { keyword: string, limit: number }): string { + headerParamFilter = filter; + return `hello`; + } - @Post("/photos") - postPhotos(@UploadedFiles("photos") files: any): any { - uploadedFilesFirstName = files[0].originalname; - uploadedFilesSecondName = files[1].originalname; - return `${uploadedFilesFirstName} ${uploadedFilesSecondName}`; - } + @Get("/questions") + getQuestions(@CookieParam("token") token: string, + @CookieParam("count") count: number, + @CookieParam("showAll") showAll: boolean): string { + cookieParamToken = token; + cookieParamCount = count; + cookieParamShowAll = showAll; + return `hello`; + } - @Post("/photos-with-limit") - postPhotosWithLimit(@UploadedFiles("photos", { options: { limits: { files: 1 } } }) files: any): any { - return `${files[0].originalname}`; - } + @Get("/questions-with-required") + getQuestionsWithIdRequired(@CookieParam("limit", {required: true}) limit: number): string { + cookieParamLimit = limit; + return `hello`; + } - @Post("/photos-with-required") - postPhotosWithRequired(@UploadedFiles("photos", { required: true }) files: any): any { - return `${files[0].originalname}`; - } + @Get("/questions-with-json") + getQuestionsWithJsonParam(@CookieParam("filter", {parse: true}) filter: { keyword: string, limit: number }): string { + cookieParamFilter = filter; + return `hello`; + } + @Post("/questions") + postQuestion(@Body() question: string): string { + body = question; + return `hello`; } - @JsonController() - class SecondUserActionParamsController { + @Post("/questions-with-required") + postRequiredQuestion(@Body({required: true}) question: string): string { + body = question; + return `hello`; + } + @Post("/form-data-body") + @UseBefore(urlencodedParser) + postFormDataBody(@Body() body: any): string { + return body.testObject.testNested.testString; + } - @Post("/posts") - postPost(@Body() question: any) { - body = question; - return body; - } + @Post("/file") + postFile(@UploadedFile("myFile") file: Express.Multer.File): string { + return `${file.originalname}`; + } - @Post("/posts-with-required") - postRequiredPost(@Body({ required: true }) post: string) { - body = post; - return body; - } + @Post("/file-with-body") + postFileWithBody(@UploadedFile("myFile") file: Express.Multer.File, @Body() body: any): string { + return `${file.originalname} - ${JSON.stringify(body)}`; + } - @Get("/posts-after") - getPhotosAfter(@QueryParam("from", { required: true }) from: Date): any { - return from.toISOString(); - } + @Post("/file-with-body-param") + postFileWithBodyParam(@UploadedFile("myFile") file: Express.Multer.File, @BodyParam("testParam") testParam: string): string { + return `${file.originalname} - ${testParam}`; + } - @Post("/users") - postUser(@BodyParam("name") name: string, - @BodyParam("age") age: number, - @BodyParam("isActive") isActive: boolean): any { - bodyParamName = name; - bodyParamAge = age; - bodyParamIsActive = isActive; - return null; - } + @Post("/file-with-limit") + postFileWithLimit(@UploadedFile("myFile", {options: {limits: {fileSize: 2}}}) file: Express.Multer.File): string { + return `${file.originalname}`; + } - @Post("/users-with-required") - postUserWithRequired(@BodyParam("name", { required: true }) name: string, - @BodyParam("age", { required: true }) age: number, - @BodyParam("isActive", { required: true }) isActive: boolean): any { - bodyParamName = name; - bodyParamAge = age; - bodyParamIsActive = isActive; - return null; - } + @Post("/file-with-required") + postFileWithRequired(@UploadedFile("myFile", {required: true}) file: Express.Multer.File): string { + return `${file.originalname}`; } - }); + @Post("/photos") + postPhotos(@UploadedFiles("photos") files: Express.Multer.File[]): string { + return `${files[0].originalname} ${files[1].originalname}`; + } - let expressApp: any, koaApp: any; - before(done => { - expressApp = createExpressServer().listen(3001, done); - }); - after(done => expressApp.close(done)); - before(done => { - koaApp = createKoaServer(); - koaApp.keys = ["koa-session-secret"]; - koaApp = koaApp.listen(3002, done); - }); - after(done => koaApp.close(done)); - - describe("@Req and @Res should be provided as Request and Response objects", () => { - assertRequest([3001, 3002], "get", "users", response => { - expect(requestReq).to.be.instanceOf(Object); // apply better check here - expect(requestRes).to.be.instanceOf(Object); // apply better check here - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - }); + @Post("/photos-with-limit") + postPhotosWithLimit(@UploadedFiles("photos", {options: {limits: {files: 1}}}) files: any): string { + return `${files[0].originalname}`; + } - describe("writing directly to the response using @Res should work", () => { - assertRequest([3001, 3002], "get", "users-direct", response => { - expect(response).to.be.status(201); - expect(response.body).to.be.equal("hi, I was written directly to the response"); - expect(response).to.have.header("content-type", "custom/x-sample; charset=utf-8"); - }); - }); + @Post("/photos-with-required") + postPhotosWithRequired(@UploadedFiles("photos", {required: true}) files: any): string { + return `${files[0].originalname}`; + } + } + + @JsonController() + class SecondUserActionParamsController { + @Post("/posts") + postPost(@Body() question: any) { + body = question; + return body; + } - describe("writing directly to the response using @Ctx should work", () => { - assertRequest([3002], "get", "users-direct/ctx", response => { - expect(response).to.be.status(201); - expect(response.body).to.be.equal("hi, I was written directly to the response using Koa Ctx"); - expect(response).to.have.header("content-type", "custom/x-sample; charset=utf-8"); - }); - }); + @Post("/posts-with-required") + postRequiredPost(@Body({required: true}) post: string) { + body = post; + return body; + } - describe("@Param should give a param from route", () => { - assertRequest([3001, 3002], "get", "users/1", response => { - expect(paramUserId).to.be.equal(1); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("1"); - }); - }); + @Get("/posts-after") + getPostsAfter(@QueryParam("from", {required: true}) from: Date): any { + return from.toISOString(); + } - describe("multiple @Param should give a proper values from route", () => { - assertRequest([3001, 3002], "get", "users/23/photos/32", response => { - expect(paramFirstId).to.be.equal(23); - expect(paramSecondId).to.be.equal(32); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("23,32"); - }); - }); + @Post("/users") + postUser(@BodyParam("name") name: string, + @BodyParam("age") age: number, + @BodyParam("isActive") isActive: boolean): any { + bodyParamName = name; + bodyParamAge = age; + bodyParamIsActive = isActive; + return null; + } - describe("@Session middleware not use", () => { - assertRequest([3001, 3002], "get", "not-use-session", response => { - expect(response).to.be.status(500); - }); - }); + @Post("/users-with-required") + postUserWithRequired(@BodyParam("name", {required: true}) name: string, + @BodyParam("age", {required: true}) age: number, + @BodyParam("isActive", {required: true}) isActive: boolean): any { + bodyParamName = name; + bodyParamAge = age; + bodyParamIsActive = isActive; + return null; + } + } - describe("@Session should return a value from session", () => { - assertRequest([3001, 3002], "post", "session", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("@Session"); - assertRequest([3001, 3002], "get", "session", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("@Session test"); - expect(sessionTestElement).to.be.equal("@Session test"); + expressServer = createExpressServer({ + cors: { + origin: "http://localhost:3001", + credentials: true + } + }).listen(3001, done); +}); +afterAll((done: DoneCallback) => expressServer.close(done)); + +it("@Req and @Res should be provided as Request and Response objects", () => { + expect.assertions(4); + return axios.get("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("hello"); // apply better check here + expect(expressResponse).toBeInstanceOf(ServerResponse); // apply better check here + }); +}); + +it("@Res writing directly to the response should work", () => { + expect.assertions(3); + return axios.get("/users-direct").then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.CREATED); + expect(response.headers["content-type"]).toEqual("custom/x-sample; charset=utf-8"); + expect(response.data).toEqual("hi, I was written directly to the response"); + }); +}); + +it("@Param should give a param from route", () => { + expect.assertions(4); + return axios.get("users/1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(paramUserId).toEqual(1); + expect(response.data).toEqual("1"); + }); +}); + +it("@Param multiple params should give a proper values from route", () => { + expect.assertions(5); + return axios.get("/users/23/photos/32") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(paramFirstId).toEqual(23); + expect(paramSecondId).toEqual(32); + expect(response.data).toEqual("23,32"); + }); +}); + +it("@SessionParam without middleware", () => { + expect.assertions(1); + return axios.get("/not-use-session") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR) + }); +}); + +it("@Session should return a value from session", () => { + expect.assertions(7); + return axios.post("/session") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("@Session"); + return axios.get("/session", { + withCredentials: true, + headers: { + cookie: response.headers["set-cookie"][0] + } + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("@Session test"); + expect(sessionTestElement).toEqual("@Session test"); }); }); +}); + +it("@Session(param) should allow to inject empty property", () => { + return axios.get("/session-param-empty") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("true"); + expect(sessionTestElement).toBeUndefined(); + }); +}); + +/* +// This test currently fails with an ECONNRESET +// See this Github issue: +// https://github.com/typestack/routing-controllers/issues/243 +it("@Session(param) should throw required error when param is empty", () => { + expect.assertions(1); + return axios.get("/session-param-empty-error", { + withCredentials: true + }).then((response: AxiosResponse) => { + // Do nothing + }).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }); +}); +*/ + +it("@QueryParams should give a proper values from request's query parameters", () => { + expect.assertions(6); + return axios.get("/photos-params?sortBy=name&count=2&limit=10&showAll") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(queryParams1.sortBy).toEqual("name"); + expect(queryParams1.count).toEqual("2"); + expect(queryParams1.limit).toEqual(10); + expect(queryParams1.showAll).toEqual(true); + }); +}); + +it("@QueryParams should give a proper values from request's query parameters with nested json", () => { + expect.assertions(9); + return axios.get("/photos-params?sortBy=name&count=2&limit=10&showAll&myObject=%7B%22num%22%3A%205,%20%22str%22%3A%20%22five%22,%20%22isFive%22%3A%20true%7D") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(queryParams1.sortBy).toEqual("name"); + expect(queryParams1.count).toEqual("2"); + expect(queryParams1.limit).toEqual(10); + expect(queryParams1.showAll).toEqual(true); + expect(queryParams1.myObject.num).toEqual(5); + expect(queryParams1.myObject.str).toEqual("five"); + expect(queryParams1.myObject.isFive).toEqual(true); + }); +}); + +it("@QueryParams should not validate request query parameters when it's turned off in validator options", () => { + expect.assertions(6); + return axios.get("/photos-params-no-validate?sortBy=verylongtext&count=2&limit=1&showAll=true") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(queryParams2.sortBy).toEqual("verylongtext"); + expect(queryParams2.count).toEqual("2"); + expect(queryParams2.limit).toEqual(1); + expect(queryParams2.showAll).toEqual(true); + }); +}); + +it("@QueryParams should give a proper values from request's optional query parameters", () => { + expect.assertions(6); + return axios.get("/photos-params-optional?sortBy=name&limit=10") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(queryParams3.sortBy).toEqual("name"); + expect(queryParams3.count).toEqual(undefined); + expect(queryParams3.limit).toEqual(10); + expect(queryParams3.showAll).toEqual(true); + }); +}); + +it("@QueryParam should give a proper values from request query parameters", () => { + expect.assertions(6); + return axios.get("/photos?sortBy=name&count=2&limit=10&showAll=true") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(queryParamSortBy).toEqual("name"); + expect(queryParamCount).toEqual("2"); + expect(queryParamLimit).toEqual(10); + expect(queryParamShowAll).toEqual(true); + }); +}); + +it("@QueryParam when required params must be provided and they should not be empty", () => { + expect.assertions(6); + return Promise.all([ + axios.get("/photos-with-required?limit=0") + .then((response: AxiosResponse) => { + expect(queryParamLimit).toEqual(0); + expect(response.status).toBe(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("0"); + }), + axios.get("/photos-with-required?") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }), + axios.get("/photos-with-required?limit") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }) + ]); +}); + +it("@QueryParam when the type is Date then it should be parsed", () => { + expect.assertions(2); + return axios.get("/posts-after/?from=2017-01-01T00:00:00Z") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual("2017-01-01T00:00:00.000Z"); + }); +}); + +it("@QueryParam when the type is Date and it is invalid then the response should be a BadRequest error", () => { + expect.assertions(2); + return axios.get("/posts-after/?from=InvalidDate") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + expect(error.response.data.name).toEqual("ParamNormalizationError"); + }); +}); + +it("@QueryParam when parseJson flag is used query param must be converted to object", () => { + expect.assertions(3); + return axios.get("/photos-with-json/?filter={\"keyword\": \"name\", \"limit\": 5}") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(queryParamFilter).toEqual({keyword: "name", limit: 5}); + }); +}); + +it("@HeaderParam should give a proper values from request headers", () => { + return axios.get("/posts", { + headers: { + token: "31ds31das231sad12", + count: 20, + showAll: false + } + }).then((response: AxiosResponse) => { + expect(headerParamToken).toEqual("31ds31das231sad12"); + expect(headerParamCount).toEqual(20); + expect(headerParamShowAll).toEqual(false); + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + }); +}); + +it("@HeaderParam when required is params must be provided and they should not be empty", () => { + expect.assertions(3); + return axios.get("/posts-with-required", { + headers: { + limit: 0 + } + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(headerParamLimit).toEqual(0); + }); +}); + +it("@HeaderParam should fail with invalid request options", () => { + expect.assertions(1); + return axios.get("/posts-with-required", { + headers: { + filter: "" + } + }).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); }); +}); - describe("@Session(param) should allow to inject empty property", () => { - assertRequest([3001, 3002], "get", "session-param-empty", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("true"); - expect(sessionTestElement).to.be.undefined; - }); - }); - - // TODO: uncomment this after we get rid of calling `next(err)` - - // describe("@Session(param) should throw required error when param is empty", () => { - // assertRequest([3001, 3002], "get", "session-param-empty-error", response => { - // expect(response).to.be.status(400); - // // there should be a test for "ParamRequiredError" but chakram is the worst testing framework ever!!! - // }); - // }); - - describe("@State should return a value from state", () => { - assertRequest([3001], "get", "state", response => { - expect(response).to.be.status(500); - }); - assertRequest([3001], "get", "state/username", response => { - expect(response).to.be.status(500); - }); - assertRequest([3002], "get", "state", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "application/json"); - expect(response.body.username).to.be.equal("pleerock"); - }); - assertRequest([3002], "get", "state/username", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("pleerock"); - }); - }); - - // todo: enable koa test when #227 fixed - describe("@QueryParams should give a proper values from request's query parameters", () => { - assertRequest([3001, /*3002*/], "get", "photos-params?sortBy=name&count=2&limit=10&showAll", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(queryParams1.sortBy).to.be.equal("name"); - expect(queryParams1.count).to.be.equal("2"); - expect(queryParams1.limit).to.be.equal(10); - expect(queryParams1.showAll).to.be.equal(true); - }); - }); - - describe("@QueryParams should give a proper values from request's query parameters with nested json", () => { - assertRequest([3001, /*3002*/], "get", "photos-params?sortBy=name&count=2&limit=10&showAll&myObject=%7B%22num%22%3A%205,%20%22str%22%3A%20%22five%22,%20%22isFive%22%3A%20true%7D", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(queryParams1.sortBy).to.be.equal("name"); - expect(queryParams1.count).to.be.equal("2"); - expect(queryParams1.limit).to.be.equal(10); - expect(queryParams1.showAll).to.be.equal(true); - expect(queryParams1.myObject.num).to.be.equal(5); - expect(queryParams1.myObject.str).to.be.equal("five"); - expect(queryParams1.myObject.isFive).to.be.equal(true); - }); - }); - - describe("@QueryParams should not validate request query parameters when it's turned off in validator options", () => { - assertRequest([3001, 3002], "get", "photos-params-no-validate?sortBy=verylongtext&count=2&limit=1&showAll=true", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(queryParams2.sortBy).to.be.equal("verylongtext"); - expect(queryParams2.count).to.be.equal("2"); - expect(queryParams2.limit).to.be.equal(1); - expect(queryParams2.showAll).to.be.equal(true); - }); - }); - - // todo: enable koa test when #227 fixed - describe("@QueryParams should give a proper values from request's optional query parameters", () => { - assertRequest([3001, /*3002*/], "get", "photos-params-optional?sortBy=name&limit=10", response => { - expect(queryParams3.sortBy).to.be.equal("name"); - expect(queryParams3.count).to.be.equal(undefined); - expect(queryParams3.limit).to.be.equal(10); - expect(queryParams3.showAll).to.be.equal(true); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - }); - - describe("@QueryParam should give a proper values from request query parameters", () => { - assertRequest([3001, 3002], "get", "photos?sortBy=name&count=2&limit=10&showAll=true", response => { - expect(queryParamSortBy).to.be.equal("name"); - expect(queryParamCount).to.be.equal("2"); - expect(queryParamLimit).to.be.equal(10); - expect(queryParamShowAll).to.be.equal(true); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - }); - - describe("for @QueryParam when required is params must be provided and they should not be empty", () => { - assertRequest([3001, 3002], "get", "photos-with-required/?limit=0", response => { - expect(queryParamLimit).to.be.equal(0); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("0"); - }); - assertRequest([3001, 3002], "get", "photos-with-required/?", response => { - expect(response).to.be.status(400); - }); - assertRequest([3001, 3002], "get", "photos-with-required/?limit", response => { - expect(response).to.be.status(400); - }); - }); - - describe("for @QueryParam when the type is Date then it should be parsed", () => { - assertRequest([3001, 3002], "get", "posts-after/?from=2017-01-01T00:00:00Z", response => { - expect(response).to.be.status(200); - expect(response.body).to.be.equal("2017-01-01T00:00:00.000Z"); - }); - }); - - describe("for @QueryParam when the type is Date and it is invalid then the response should be a BadRequest error", () => { - assertRequest([3001, 3002], "get", "posts-after/?from=InvalidDate", response => { - expect(response).to.be.status(400); - expect(response.body.name).to.be.equals("ParamNormalizationError"); - }); - }); - - describe("for @QueryParam when parseJson flag is used query param must be converted to object", () => { - assertRequest([3001, 3002], "get", "photos-with-json/?filter={\"keyword\": \"name\", \"limit\": 5}", response => { - expect(queryParamFilter).to.be.eql({ keyword: "name", limit: 5 }); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - }); - - describe("@HeaderParam should give a proper values from request headers", () => { - const requestOptions = { - headers: { - token: "31ds31das231sad12", - count: 20, - showAll: false - } - }; - assertRequest([3001, 3002], "get", "posts", requestOptions, response => { - expect(headerParamToken).to.be.equal("31ds31das231sad12"); - expect(headerParamCount).to.be.equal(20); - expect(headerParamShowAll).to.be.equal(false); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); +it("@HeaderParam should fail with missing required params", () => { + expect.assertions(1); + return axios.get("/posts-with-required") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); }); - }); +}); - describe("for @HeaderParam when required is params must be provided and they should not be empty", () => { - const validRequestOptions = { - headers: { - limit: 0 - } - }; - const invalidRequestOptions = { +it("for @HeaderParam when parseJson flag is used query param must be converted to object", () => { + expect.assertions(3); + return axios.get("/posts-with-json", { + headers: { + filter: "{\"keyword\": \"name\", \"limit\": 5}" + } + }).then((response: AxiosResponse) => { + expect(headerParamFilter).toEqual({keyword: "name", limit: 5}); + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + }); +}); + +it("@CookieParam should give a proper values from request headers", () => { + expect.assertions(5); + return axios.get("/questions", { + headers: { + Cookie: "token=31ds31das231sad12; count=20; showAll=false" + }, + withCredentials: true + }).then((response: AxiosResponse) => { + expect(cookieParamToken).toEqual("31ds31das231sad12"); + expect(cookieParamCount).toEqual(20); + expect(cookieParamShowAll).toEqual(false); + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + }); +}); + +it("@CookieParam when required is params must be provided and they should not be empty", () => { + expect.assertions(4); + return Promise.all([ + axios.get("/questions-with-required", { headers: { - filter: "" - } - }; - assertRequest([3001, 3002], "get", "posts-with-required", validRequestOptions, response => { - expect(headerParamLimit).to.be.equal(0); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - assertRequest([3001, 3002], "get", "posts-with-required", invalidRequestOptions, response => { - expect(response).to.be.status(400); - }); - assertRequest([3001, 3002], "get", "posts-with-required", response => { - expect(response).to.be.status(400); - }); + Cookie: "limit=20" + }, + withCredentials: true + }).then((response: AxiosResponse) => { + expect(cookieParamLimit).toEqual(20); + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + }), + axios.get("questions-with-required") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }) + ]); +}); + +it("@CookieParam when parseJson flag is used query param must be converted to object", () => { + expect.assertions(3); + return axios.get("/questions-with-json", { + headers: { + Cookie: "filter={\"keyword\": \"name\", \"limit\": 5}" + }, + withCredentials: true + }).then((response: AxiosResponse) => { + expect(cookieParamFilter).toEqual({keyword: "name", limit: 5}); + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + }); +}); + +it("@Body should provide a request body", () => { + expect.assertions(3); + return axios.post("/questions", "hello", { + headers: { + "Content-type": "text/plain" + } + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("hello"); }); +}); - describe("for @HeaderParam when parseJson flag is used query param must be converted to object", () => { - const requestOptions = { +it("@Body should fail if required body was not provided", () => { + expect.assertions(5); + return Promise.all([ + axios.post("/questions-with-required", "0", { headers: { - filter: "{\"keyword\": \"name\", \"limit\": 5}" + "Content-type": "text/plain" } - }; - assertRequest([3001, 3002], "get", "posts-with-json", requestOptions, response => { - expect(headerParamFilter).to.be.eql({ keyword: "name", limit: 5 }); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - }); - - describe("@CookieParam should give a proper values from request headers", () => { - const request = require("request"); - const jar = request.jar(); - const url2 = "http://127.0.0.1:3002/questions"; - jar.setCookie(request.cookie("token=31ds31das231sad12"), url2); - jar.setCookie(request.cookie("count=20"), url2); - jar.setCookie(request.cookie("showAll=false"), url2); - - const requestOptions = { - jar: jar - }; - assertRequest([3001, 3002], "get", "questions", requestOptions, response => { - expect(cookieParamToken).to.be.equal("31ds31das231sad12"); - expect(cookieParamCount).to.be.equal(20); - expect(cookieParamShowAll).to.be.equal(false); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - }); - - describe("for @CookieParam when required is params must be provided and they should not be empty", () => { - const request = require("request"); - const jar = request.jar(); - const url = "http://127.0.0.1:3001/questions-with-required"; - jar.setCookie(request.cookie("limit=20"), url); - - const validRequestOptions = { jar: jar }; - const invalidRequestOptions = { jar: request.jar() }; - - assertRequest([3001, 3002], "get", "questions-with-required", validRequestOptions, response => { - expect(cookieParamLimit).to.be.equal(20); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - - assertRequest([3001, 3002], "get", "questions-with-required", invalidRequestOptions, response => { - expect(response).to.be.status(400); - }); - }); - - describe("for @CookieParam when parseJson flag is used query param must be converted to object", () => { - const request = require("request"); - const jar = request.jar(); - const url = "http://127.0.0.1:3001/questions-with-json"; - jar.setCookie(request.cookie("filter={\"keyword\": \"name\", \"limit\": 5}"), url); - const requestOptions = { jar: jar }; - - assertRequest([3001, 3002], "get", "questions-with-json", requestOptions, response => { - expect(cookieParamFilter).to.be.eql({ keyword: "name", limit: 5 }); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - }); - - describe("@Body should provide a request body", () => { - const requestOptions = { + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(body).toEqual("0"); + }), + axios.post("/questions-with-required", "", { headers: { "Content-type": "text/plain" - }, - json: false - }; - - // todo: koa @Body with text bug. uncomment after fix https://github.com/koajs/bodyparser/issues/52 - assertRequest([3001/*, 3002*/], "post", "questions", "hello", requestOptions, response => { - expect(body).to.be.equal("hello"); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - }); - - // todo: koa @Body with text bug. uncomment after fix https://github.com/koajs/bodyparser/issues/52 - describe("@Body should fail if required body was not provided", () => { - const requestOptions = { + } + }).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }), + axios.post("/questions-with-required", { headers: { "Content-type": "text/plain" - }, - json: false - }; - - assertRequest([3001/*, 3002*/], "post", "questions-with-required", "0", requestOptions, response => { - expect(body).to.be.equal("0"); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - }); - - assertRequest([3001, 3002], "post", "questions-with-required", "", requestOptions, response => { - expect(response).to.be.status(400); - }); - - assertRequest([3001, 3002], "post", "questions-with-required", undefined, requestOptions, response => { - expect(response).to.be.status(400); - }); - }); - - describe("@Body should provide a json object for json-typed controllers and actions", () => { - assertRequest([3001, 3002], "post", "posts", { hello: "world" }, response => { - expect(body).to.be.eql({ hello: "world" }); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql(body); // should we allow to return a text body for json controllers? - }); - }); - - describe("@Body should fail if required body was not provided for json-typed controllers and actions", () => { - assertRequest([3001, 3002], "post", "posts-with-required", { hello: "" }, response => { - expect(response).to.be.status(200); - }); - assertRequest([3001, 3002], "post", "posts-with-required", undefined, response => { - expect(response).to.be.status(400); - }); - }); - - describe("@BodyParam should provide a json object for json-typed controllers and actions", () => { - assertRequest([3001, 3002], "post", "users", { name: "johny", age: 27, isActive: true }, response => { - expect(bodyParamName).to.be.eql("johny"); - expect(bodyParamAge).to.be.eql(27); - expect(bodyParamIsActive).to.be.eql(true); - expect(response).to.be.status(204); - }); - }); - - describe("@BodyParam should fail if required body was not provided for json-typed controllers and actions", () => { - - assertRequest([3001, 3002], "post", "users-with-required", { name: "johny", age: 27, isActive: true }, response => { - expect(response).to.be.status(204); - }); - assertRequest([3001, 3002], "post", "users-with-required", undefined, response => { - expect(response).to.be.status(400); - }); - assertRequest([3001, 3002], "post", "users-with-required", { name: "", age: 27, isActive: false }, response => { - expect(response).to.be.status(400); - }); - assertRequest([3001, 3002], "post", "users-with-required", { name: "Johny", age: 0, isActive: false }, response => { - expect(response).to.be.status(204); - }); - assertRequest([3001, 3002], "post", "users-with-required", { name: "Johny", age: undefined, isActive: false }, response => { - expect(response).to.be.status(400); - }); - assertRequest([3001, 3002], "post", "users-with-required", { name: "Johny", age: 27, isActive: undefined }, response => { - expect(response).to.be.status(400); - }); - assertRequest([3001, 3002], "post", "users-with-required", { name: "Johny", age: 27, isActive: false }, response => { - expect(response).to.be.status(204); - }); - assertRequest([3001, 3002], "post", "users-with-required", { name: "Johny", age: 27, isActive: true }, response => { - expect(response).to.be.status(204); - }); - }); - - describe("@UploadedFile should provide uploaded file with the given name", () => { - const requestOptions = { - formData: { - myfile: { - value: "hello world", - options: { - filename: "hello-world.txt", - contentType: "image/text" - } - } } - }; - - assertRequest([3001, 3002], "post", "files", undefined, requestOptions, response => { - expect(uploadedFileName).to.be.eql("hello-world.txt"); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("hello-world.txt"); - }); - }); - - describe("@UploadedFile with @Body should return both the file and the body", () => { - const requestOptions = { - formData: { - myfile: { - value: "hello world", - options: { - filename: "hello-world.txt", - contentType: "image/text" - } - }, - anotherField: "hi", - andOther: "hello", + }).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }) + ]); +}); + +it("@Body should provide a json object for json-typed controllers and actions", () => { + expect.assertions(4); + return axios.post("/posts", {hello: "world"}) + .then((response: AxiosResponse) => { + expect(body).toEqual({hello: "world"}); + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual(body); // should we allow to return a text body for json controllers? + }); +}); + +it("@Body should fail if required body was not provided for json-typed controllers and actions", () => { + expect.assertions(2); + return Promise.all([ + axios.post("posts-with-required", {hello: ""}) + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + }), + axios.post("posts-with-required") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }) + ]); +}); + +it("@BodyParam should provide a json object for json-typed controllers and actions", () => { + expect.assertions(4); + return axios.post("/users", {name: "johny", age: 27, isActive: true}) + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.NO_CONTENT); + expect(bodyParamName).toEqual("johny"); + expect(bodyParamAge).toEqual(27); + expect(bodyParamIsActive).toEqual(true); + }); +}); + +it("@BodyParam should fail if required body was not provided for json-typed controllers and actions", () => { + expect.assertions(8); + return Promise.all([ + axios.post("/users-with-required", {name: "johny", age: 27, isActive: true}) + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.NO_CONTENT); + }), + axios.post("/users-with-required") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }), + axios.post("/users-with-required", {name: "", age: 27, isActive: false}) + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }), + axios.post("/users-with-required", {name: "Johny", age: 0, isActive: false}) + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.NO_CONTENT); + }), + axios.post("/users-with-required", {name: "Johny", age: undefined, isActive: false}) + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }), + axios.post("/users-with-required", {name: "Johny", age: 27, isActive: undefined}) + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }), + axios.post("/users-with-required", {name: "Johny", age: 27, isActive: false}) + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.NO_CONTENT); + }), + axios.post("/users-with-required", {name: "Johny", age: 27, isActive: true}) + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.NO_CONTENT); + }) + ]); +}); + +it("@Body using application/x-www-form-urlencoded should handle url encoded form data", () => { + expect.assertions(3); + return axios.post("/form-data-body", qs.stringify({ + testObject: { + testNested: { + testString: "this is a urlencoded form-data test", + testNumber: 5 } - }; - - assertRequest([3001, 3002], "post", "files-with-body", undefined, requestOptions, response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal(`hello-world.txt - {"anotherField":"hi","andOther":"hello"}`); - }); - }); - - describe("@UploadedFile with @BodyParam should return both the file and the body param", () => { - const requestOptions = { - formData: { - myfile: { - value: "hello world", - options: { - filename: "hello-world.txt", - contentType: "image/text" - } - }, - p1: "hi, i'm a param", - } - }; - - assertRequest([3001, 3002], "post", "files-with-body-param", undefined, requestOptions, response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("hello-world.txt - hi, i'm a param"); - }); - }); - - describe("@UploadedFile with passed uploading options (limit) should throw an error", () => { - const validRequestOptions = { - formData: { - myfile: { - value: "a", - options: { - filename: "hello-world.txt", - contentType: "image/text" - } - } - } - }; - const invalidRequestOptions = { - formData: { - myfile: { - value: "hello world", - options: { - filename: "hello-world.txt", - contentType: "image/text" - } - } - } - }; - - assertRequest([3001, 3002], "post", "files-with-limit", undefined, validRequestOptions, response => { - expect(response).to.be.status(200); - }); - - assertRequest([3001, 3002], "post", "files-with-limit", undefined, invalidRequestOptions, response => { - expect(response).to.be.status(500); - }); - }); - - describe("for @UploadedFile when required is used files must be provided", () => { - const requestOptions = { - formData: { - myfile: { - value: "hello world", - options: { - filename: "hello-world.txt", - contentType: "image/text" - } - } - } - }; - - assertRequest([3001, 3002], "post", "files-with-required", undefined, requestOptions, response => { - expect(response).to.be.status(200); - }); - - assertRequest([3001, 3002], "post", "files-with-required", undefined, {}, response => { - expect(response).to.be.status(400); - }); - }); - - describe("@UploadedFiles should provide uploaded files with the given name", () => { - const requestOptions = { - formData: { - photos: [{ - value: "0110001", - options: { - filename: "me.jpg", - contentType: "image/jpg" - } - }, { - value: "10011010", - options: { - filename: "she.jpg", - contentType: "image/jpg" - } - }] - } - }; - - assertRequest([3001, 3002], "post", "photos", undefined, requestOptions, response => { - expect(uploadedFilesFirstName).to.be.eql("me.jpg"); - expect(uploadedFilesSecondName).to.be.eql("she.jpg"); - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("me.jpg she.jpg"); - }); - }); - - describe("@UploadedFiles with passed uploading options (limit) should throw an error", () => { - const validRequestOptions = { - formData: { - photos: [{ - value: "0110001", - options: { - filename: "me.jpg", - contentType: "image/jpg" - } - }] - } - }; - const invalidRequestOptions = { - formData: { - photos: [{ - value: "0110001", - options: { - filename: "me.jpg", - contentType: "image/jpg" - } - }, { - value: "10011010", - options: { - filename: "she.jpg", - contentType: "image/jpg" - } - }] - } - }; - - assertRequest([3001, 3002], "post", "photos-with-limit", undefined, validRequestOptions, response => { - expect(response).to.be.status(200); - }); - assertRequest([3001, 3002], "post", "photos-with-limit", undefined, invalidRequestOptions, response => { - expect(response).to.be.status(500); - }); - }); - - describe("for @UploadedFiles when required is used files must be provided", () => { - const requestOptions = { - formData: { - photos: [{ - value: "0110001", - options: { - filename: "me.jpg", - contentType: "image/jpg" - } - }, { - value: "10011010", - options: { - filename: "she.jpg", - contentType: "image/jpg" - } - }] - } - }; - - assertRequest([3001, 3002], "post", "photos-with-required", undefined, requestOptions, response => { - expect(response).to.be.status(200); - }); - assertRequest([3001, 3002], "post", "photos-with-required", undefined, {}, response => { - expect(response).to.be.status(400); - }); - - }); - -}); \ No newline at end of file + } + }), { + headers: {"Content-Type": "application/x-www-form-urlencoded"} + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("this is a urlencoded form-data test"); + }); +}); + +it("@UploadedFile using multipart/form-data should provide uploaded file with the given name", () => { + expect.assertions(3); + let form = new FormData(); + form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); + return axios.post("/file", form, { + headers: form.getHeaders() + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("sample-text-file-one.txt"); + }); +}); + +it("@UploadedFile with @Body should return both the file and the body", () => { + expect.assertions(3); + let form = new FormData(); + form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); + form.append("anotherField", "hello"); + form.append("andAnother", "world"); + return axios.post("/file-with-body", form, { + headers: form.getHeaders() + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual(`sample-text-file-one.txt - {"anotherField":"hello","andAnother":"world"}`); + }); +}); + +it("@UploadedFile with @BodyParam should return both the file and the body param", () => { + expect.assertions(3); + let form = new FormData(); + form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); + form.append("testParam", "testParamOne"); + return axios.post("/file-with-body-param", form, { + headers: form.getHeaders() + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual(`sample-text-file-one.txt - testParamOne`); + }); +}); + +it("@UploadedFile with passed uploading options (limit) should throw an error", () => { + expect.assertions(1); + let form = new FormData(); + form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); + return axios.post("/file-with-limit", form, { + headers: form.getHeaders() + }).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); + }); +}); + +it("@UploadedFile when required is used files must be provided", () => { + expect.assertions(4); + let form = new FormData(); + form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); + return Promise.all([ + axios.post("/file-with-required", form, { + headers: form.getHeaders() + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("sample-text-file-one.txt"); + }), + axios.post("/file-with-required", undefined, { + headers: form.getHeaders() + }).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }) + ]); +}); + +it("@UploadedFiles should provide uploaded files with the given name", () => { + expect.assertions(3); + let form = new FormData(); + form.append("photos", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); + form.append("photos", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-two.txt"))); + return axios.post("/photos", form, { + headers: form.getHeaders() + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("sample-text-file-one.txt sample-text-file-two.txt"); + }); +}); + +it("@UploadedFiles with passed uploading options (limit) should throw an error", () => { + expect.assertions(1); + let form = new FormData(); + form.append("photos", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); + form.append("photos", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-two.txt"))); + return axios.post("/photos-with-limit", form, { + headers: form.getHeaders() + }).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); + }); +}); + +it("@UploadedFiles when required is used files must be provided", () => { + expect.assertions(1); + let form = new FormData(); + return axios.post("/photos-with-required", undefined, { + headers: form.getHeaders() + }).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); + }); +}); diff --git a/test/functional/auth-decorator.spec.ts b/test/functional/auth-decorator.spec.ts index a7bbfb27..c087c633 100644 --- a/test/functional/auth-decorator.spec.ts +++ b/test/functional/auth-decorator.spec.ts @@ -1,177 +1,156 @@ import "reflect-metadata"; import {Get} from "../../src/decorator/Get"; -import { createExpressServer, createKoaServer, getMetadataArgsStorage, NotAcceptableError } from "../../src/index"; -import {assertRequest} from "./test-utils"; +import {createExpressServer, getMetadataArgsStorage, NotAcceptableError} from "../../src/index"; import {JsonController} from "../../src/decorator/JsonController"; import {Authorized} from "../../src/decorator/Authorized"; import {Action} from "../../src/Action"; -import {RoutingControllersOptions} from "../../src/RoutingControllersOptions"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {Server as HttpServer} from "http"; +import DoneCallback = jest.DoneCallback; +import {AxiosError, AxiosResponse} from "axios"; +import HttpStatusCodes from "http-status-codes"; +import {axios} from "../utilities/axios"; const sleep = (time: number) => new Promise(resolve => setTimeout(resolve, time)); describe("Controller responds with value when Authorization succeeds (async)", function () { + let expressServer: HttpServer; - before(() => { - - // reset metadata args storage + beforeEach((done: DoneCallback) => { getMetadataArgsStorage().reset(); @JsonController() class AuthController { - @Authorized() @Get("/auth1") auth1() { - return { test: "auth1" }; + return {test: "auth1"}; } @Authorized(["role1"]) @Get("/auth2") auth2() { - return { test: "auth2" }; + return {test: "auth2"}; } @Authorized() @Get("/auth3") async auth3() { await sleep(10); - return { test: "auth3" }; + return {test: "auth3"}; } - } - }); - const serverOptions: RoutingControllersOptions = { - authorizationChecker: async (action: Action, roles?: string[]) => { - await sleep(10); - return true; - } - }; - - let expressApp: any; - before(done => { - const server = createExpressServer(serverOptions); - expressApp = server.listen(3001, done); + expressServer = createExpressServer({ + authorizationChecker: async (action: Action, roles?: string[]) => { + await sleep(10); + return true; + } + }).listen(3001, done); }); - after(done => expressApp.close(done)); - let koaApp: any; - before(done => { - const server = createKoaServer(serverOptions); - koaApp = server.listen(3002, done); - }); - after(done => koaApp.close(done)); + afterEach(done => expressServer.close(done)); - describe("without roles", () => { - assertRequest([3001, 3002], "get", "auth1", response => { - expect(response).to.have.status(200); - expect(response.body).to.eql({ test: "auth1" }); - }); + it("without roles", () => { + expect.assertions(2); + return axios.get("/auth1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual({test: "auth1"}); + }); }); - describe("with roles", () => { - assertRequest([3001, 3002], "get", "auth2", response => { - expect(response).to.have.status(200); - expect(response.body).to.eql({ test: "auth2" }); - }); + it("with roles", () => { + expect.assertions(2); + return axios.get("/auth2") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual({test: "auth2"}); + }); }); - describe("async", () => { - assertRequest([3001, 3002], "get", "auth3", response => { - expect(response).to.have.status(200); - expect(response.body).to.eql({ test: "auth3" }); - }); + it("async", () => { + expect.assertions(2); + return axios.get("/auth3") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual({test: "auth3"}); + }); }); - }); -describe("Controller responds with value when Authorization succeeds (sync)", function () { - - before(() => { +describe("Controller responds with value when Authorization succeeds (sync)", () => { + let expressServer: HttpServer; - // reset metadata args storage + beforeEach((done: DoneCallback) => { getMetadataArgsStorage().reset(); @JsonController() class AuthController { - @Authorized() @Get("/auth1") auth1() { - return { test: "auth1" }; + return {test: "auth1"}; } @Authorized(["role1"]) @Get("/auth2") auth2() { - return { test: "auth2" }; + return {test: "auth2"}; } @Authorized() @Get("/auth3") async auth3() { await sleep(10); - return { test: "auth3" }; + return {test: "auth3"}; } - - } - }); - - const serverOptions: RoutingControllersOptions = { - authorizationChecker: (action: Action, roles?: string[]) => { - return true; } - }; - let expressApp: any; - before(done => { - const server = createExpressServer(serverOptions); - expressApp = server.listen(3001, done); + expressServer = createExpressServer({ + authorizationChecker: async (action: Action, roles?: string[]) => { + return true; + } + }).listen(3001, done); }); - after(done => expressApp.close(done)); - let koaApp: any; - before(done => { - const server = createKoaServer(serverOptions); - koaApp = server.listen(3002, done); - }); - after(done => koaApp.close(done)); + afterEach(done => expressServer.close(done)); - describe("without roles", () => { - assertRequest([3001, 3002], "get", "auth1", response => { - expect(response).to.have.status(200); - expect(response.body).to.eql({ test: "auth1" }); - }); + it("without roles", () => { + expect.assertions(2); + return axios.get("/auth1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual({test: "auth1"}); + }); }); - describe("with roles", () => { - assertRequest([3001, 3002], "get", "auth2", response => { - expect(response).to.have.status(200); - expect(response.body).to.eql({ test: "auth2" }); - }); + it("with roles", () => { + expect.assertions(2); + return axios.get("/auth2") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual({test: "auth2"}); + }); }); - describe("async", () => { - assertRequest([3001, 3002], "get", "auth3", response => { - expect(response).to.have.status(200); - expect(response.body).to.eql({ test: "auth3" }); - }); + it("async", () => { + expect.assertions(2); + return axios.get("/auth3") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(200); + expect(response.data).toEqual({test: "auth3"}); + }); }); - }); -describe("Authorized Decorators Http Status Code", function () { - - before(() => { +describe("Authorized Decorators Http Status Code", () => { + let expressServer: HttpServer; - // reset metadata args storage + beforeEach((done: DoneCallback) => { getMetadataArgsStorage().reset(); @JsonController() class AuthController { - @Authorized() @Get("/auth1") auth1() { @@ -183,47 +162,38 @@ describe("Authorized Decorators Http Status Code", function () { auth2() { return {test: "auth2"}; } - } - }); - const serverOptions: RoutingControllersOptions = { - authorizationChecker: async (action: Action, roles?: string[]) => { - return false; - } - }; - - let expressApp: any; - before(done => { - const server = createExpressServer(serverOptions); - expressApp = server.listen(3001, done); + expressServer = createExpressServer({ + authorizationChecker: async (action: Action, roles?: string[]) => { + return false; + } + }).listen(3001, done); }); - after(done => expressApp.close(done)); - let koaApp: any; - before(done => { - const server = createKoaServer(serverOptions); - koaApp = server.listen(3002, done); - }); - after(done => koaApp.close(done)); + afterEach(done => expressServer.close(done)); - describe("without roles", () => { - assertRequest([3001, 3002], "get", "auth1", response => { - expect(response).to.have.status(401); - }); + it("without roles", () => { + expect.assertions(1); + return axios.get("/auth1") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.UNAUTHORIZED); + }); }); - describe("with roles", () => { - assertRequest([3001, 3002], "get", "auth2", response => { - expect(response).to.have.status(403); - }); + it("with roles", () => { + expect.assertions(1); + return axios.get("/auth2") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.FORBIDDEN); + }); }); - }); -describe("Authorization checker allows to throw (async)", function() { - before(() => { - // reset metadata args storage +describe("Authorization checker allows to throw (async)", () => { + let expressServer: HttpServer; + + beforeEach((done: DoneCallback) => { getMetadataArgsStorage().reset(); @JsonController() @@ -231,42 +201,34 @@ describe("Authorization checker allows to throw (async)", function() { @Authorized() @Get("/auth1") auth1() { - return { test: "auth1" }; + return {test: "auth1"}; } } - }); - const serverOptions: RoutingControllersOptions = { - authorizationChecker: async (action: Action, roles?: string[]) => { - throw new NotAcceptableError("Custom Error"); - }, - }; - - let expressApp: any; - before(done => { - const server = createExpressServer(serverOptions); - expressApp = server.listen(3001, done); + expressServer = createExpressServer({ + authorizationChecker: async (action: Action, roles?: string[]) => { + throw new NotAcceptableError("Custom Error"); + } + }).listen(3001, done); }); - after(done => expressApp.close(done)); - let koaApp: any; - before(done => { - const server = createKoaServer(serverOptions); - koaApp = server.listen(3002, done); - }); - after(done => koaApp.close(done)); - - describe("custom errors", () => { - assertRequest([3001, 3002], "get", "auth1", response => { - expect(response).to.have.status(406); - expect(response.body).to.have.property("name", "NotAcceptableError"); - expect(response.body).to.have.property("message", "Custom Error"); - }); + afterEach(done => expressServer.close(done)); + + it("custom errors", () => { + expect.assertions(3); + return axios.get("/auth1") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_ACCEPTABLE); + expect(error.response.data).toHaveProperty("name", "NotAcceptableError"); + expect(error.response.data).toHaveProperty("message", "Custom Error"); + }); }); }); -describe("Authorization checker allows to throw (sync)", function() { - before(() => { +describe("Authorization checker allows to throw (sync)", () => { + let expressServer: HttpServer; + + beforeEach((done: DoneCallback) => { // reset metadata args storage getMetadataArgsStorage().reset(); @@ -275,36 +237,26 @@ describe("Authorization checker allows to throw (sync)", function() { @Authorized() @Get("/auth1") auth1() { - return { test: "auth1" }; + return {test: "auth1"}; } } - }); - - const serverOptions: RoutingControllersOptions = { - authorizationChecker: (action: Action, roles?: string[]) => { - throw new NotAcceptableError("Custom Error"); - }, - }; - let expressApp: any; - before(done => { - const server = createExpressServer(serverOptions); - expressApp = server.listen(3001, done); + expressServer = createExpressServer({ + authorizationChecker: async (action: Action, roles?: string[]) => { + throw new NotAcceptableError("Custom Error"); + } + }).listen(3001, done); }); - after(done => expressApp.close(done)); - let koaApp: any; - before(done => { - const server = createKoaServer(serverOptions); - koaApp = server.listen(3002, done); - }); - after(done => koaApp.close(done)); - - describe("custom errors", () => { - assertRequest([3001, 3002], "get", "auth1", response => { - expect(response).to.have.status(406); - expect(response.body).to.have.property("name", "NotAcceptableError"); - expect(response.body).to.have.property("message", "Custom Error"); - }); + afterEach(done => expressServer.close(done)); + + it("custom errors", () => { + expect.assertions(3); + return axios.get("/auth1") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_ACCEPTABLE); + expect(error.response.data).toHaveProperty("name", "NotAcceptableError"); + expect(error.response.data).toHaveProperty("message", "Custom Error"); + }); }); -}); \ No newline at end of file +}); diff --git a/test/functional/class-transformer-options.spec.ts b/test/functional/class-transformer-options.spec.ts new file mode 100644 index 00000000..839c09ab --- /dev/null +++ b/test/functional/class-transformer-options.spec.ts @@ -0,0 +1,187 @@ +import "reflect-metadata"; +import qs from "qs"; +import HttpStatusCodes from "http-status-codes"; +import {JsonController} from "../../src/decorator/JsonController"; +import {createExpressServer, getMetadataArgsStorage, ResponseClassTransformOptions} from "../../src/index"; +import {Expose} from "class-transformer"; +import {Get} from "../../src/decorator/Get"; +import {QueryParam} from "../../src/decorator/QueryParam"; +import DoneCallback = jest.DoneCallback; +import {Server as HttpServer} from "http"; +import {AxiosResponse} from "axios"; +import {defaultMetadataStorage} from "class-transformer/storage"; +import {axios} from "../utilities/axios"; + +class UserFilter { + keyword: string; +} + +class UserModel { + id: number; + _firstName: string; + _lastName: string; + + @Expose() + get name(): string { + return this._firstName + " " + this._lastName; + } +} + +afterAll(() => defaultMetadataStorage.clear()); + +describe("no options", () => { + let expressServer: HttpServer; + let requestFilter: UserFilter; + + beforeEach((done: DoneCallback) => { + requestFilter = undefined; + getMetadataArgsStorage().reset(); + + @JsonController() + class UserController { + @Get("/user") + getUsers(@QueryParam("filter") filter: UserFilter): any { + requestFilter = filter; + const user = new UserModel(); + user.id = 1; + user._firstName = "Umed"; + user._lastName = "Khudoiberdiev"; + return user; + } + } + + expressServer = createExpressServer({ + validation: false + }).listen(3001, done); + }); + + afterEach((done: DoneCallback) => expressServer.close(done)); + + it("should not use any options if not set", () => { + expect.assertions(4); + return axios.get("/user?" + qs.stringify({ + filter: { + keyword: "Um", + __somethingPrivate: "blablabla" + } + })).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual({ + id: 1, + _firstName: "Umed", + _lastName: "Khudoiberdiev", + name: "Umed Khudoiberdiev" + }); + expect(requestFilter).toBeInstanceOf(UserFilter); + expect(requestFilter).toEqual({ + keyword: "Um", + __somethingPrivate: "blablabla", + }); + }); + }); +}); // ------ end no options + +describe("global options", () => { + let expressServer: HttpServer; + let requestFilter: UserFilter; + + beforeEach((done: DoneCallback) => { + requestFilter = undefined; + getMetadataArgsStorage().reset(); + + @JsonController() + class ClassTransformUserController { + @Get("/user") + getUsers(@QueryParam("filter") filter: UserFilter): any { + requestFilter = filter; + const user = new UserModel(); + user.id = 1; + user._firstName = "Umed"; + user._lastName = "Khudoiberdiev"; + return user; + } + } + + expressServer = createExpressServer({ + validation: false, + classToPlainTransformOptions: { + excludePrefixes: ["_"] + }, + plainToClassTransformOptions: { + excludePrefixes: ["__"] + } + }).listen(3001, done); + }); + + afterEach((done: DoneCallback) => expressServer.close(done)); + + it("should apply global options", () => { + expect.assertions(4); + return axios.get("/user?" + qs.stringify({ + filter: { + keyword: "Um", + __somethingPrivate: "blablabla" + } + })).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual({ + id: 1, + name: "Umed Khudoiberdiev" + }); + expect(requestFilter).toBeInstanceOf(UserFilter); + expect(requestFilter).toEqual({ + keyword: "Um" + }); + }); + }); +}); // ----- end global options + +describe("local options", () => { + let expressServer: HttpServer; + let requestFilter: UserFilter; + + beforeEach((done: DoneCallback) => { + requestFilter = undefined; + getMetadataArgsStorage().reset(); + + @JsonController() + class ClassTransformUserController { + @Get("/user") + @ResponseClassTransformOptions({excludePrefixes: ["_"]}) + getUsers(@QueryParam("filter", {transform: {excludePrefixes: ["__"]}}) filter: UserFilter): any { + requestFilter = filter; + const user = new UserModel(); + user.id = 1; + user._firstName = "Umed"; + user._lastName = "Khudoiberdiev"; + return user; + } + } + + expressServer = createExpressServer({ + validation: false + }).listen(3001, done); + }); + + afterEach((done: DoneCallback) => expressServer.close(done)); + + it("should apply local options", () => { + expect.assertions(4); + return axios.get("/user?" + qs.stringify({ + filter: { + keyword: "Um", + __somethingPrivate: "blablabla" + } + })).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual({ + id: 1, + name: "Umed Khudoiberdiev" + }); + expect(requestFilter).toBeInstanceOf(UserFilter); + expect(requestFilter).toEqual({ + keyword: "Um" + }); + }); + }); +}); //----- end local options diff --git a/test/functional/class-transformer-options.ts b/test/functional/class-transformer-options.ts deleted file mode 100644 index 40e01dce..00000000 --- a/test/functional/class-transformer-options.ts +++ /dev/null @@ -1,183 +0,0 @@ -import "reflect-metadata"; -import {JsonController} from "../../src/decorator/JsonController"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; -import {Expose} from "class-transformer"; -import {defaultMetadataStorage} from "class-transformer/storage"; -import {Get} from "../../src/decorator/Get"; -import {QueryParam} from "../../src/decorator/QueryParam"; -import {ResponseClassTransformOptions} from "../../src/decorator/ResponseClassTransformOptions"; -import {RoutingControllersOptions} from "../../src/RoutingControllersOptions"; -const chakram = require("chakram"); -const expect = chakram.expect; - -describe("class transformer options", () => { - - class UserFilter { - keyword: string; - } - - class UserModel { - id: number; - _firstName: string; - _lastName: string; - - @Expose() - get name(): string { - return this._firstName + " " + this._lastName; - } - } - - after(() => { - defaultMetadataStorage.clear(); - }); - - describe("should not use any options if not set", () => { - - let requestFilter: any; - beforeEach(() => { - requestFilter = undefined; - }); - - before(() => { - getMetadataArgsStorage().reset(); - - @JsonController() - class UserController { - - @Get("/user") - getUsers(@QueryParam("filter") filter: UserFilter): any { - requestFilter = filter; - const user = new UserModel(); - user.id = 1; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - return user; - } - - } - }); - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "user?filter={\"keyword\": \"Um\", \"__somethingPrivate\": \"blablabla\"}", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql({ - id: 1, - _firstName: "Umed", - _lastName: "Khudoiberdiev", - name: "Umed Khudoiberdiev" - }); - expect(requestFilter).to.be.instanceOf(UserFilter); - expect(requestFilter).to.be.eql({ - keyword: "Um", - __somethingPrivate: "blablabla", - }); - }); - }); - - describe("should apply global options", () => { - - let requestFilter: any; - beforeEach(() => { - requestFilter = undefined; - }); - - before(() => { - getMetadataArgsStorage().reset(); - - @JsonController() - class ClassTransformUserController { - - @Get("/user") - getUsers(@QueryParam("filter") filter: UserFilter): any { - requestFilter = filter; - const user = new UserModel(); - user.id = 1; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - return user; - } - - } - }); - - const options: RoutingControllersOptions = { - classToPlainTransformOptions: { - excludePrefixes: ["_"] - }, - plainToClassTransformOptions: { - excludePrefixes: ["__"] - } - }; - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer(options).listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer(options).listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "user?filter={\"keyword\": \"Um\", \"__somethingPrivate\": \"blablabla\"}", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql({ - id: 1, - name: "Umed Khudoiberdiev" - }); - expect(requestFilter).to.be.instanceOf(UserFilter); - expect(requestFilter).to.be.eql({ - keyword: "Um" - }); - }); - }); - - describe("should apply local options", () => { - - let requestFilter: any; - beforeEach(() => { - requestFilter = undefined; - }); - - before(() => { - getMetadataArgsStorage().reset(); - - @JsonController() - class ClassTransformUserController { - - @Get("/user") - @ResponseClassTransformOptions({ excludePrefixes: ["_"] }) - getUsers(@QueryParam("filter", { transform: { excludePrefixes: ["__"] } }) filter: UserFilter): any { - requestFilter = filter; - const user = new UserModel(); - user.id = 1; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - return user; - } - - } - }); - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "user?filter={\"keyword\": \"Um\", \"__somethingPrivate\": \"blablabla\"}", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql({ - id: 1, - name: "Umed Khudoiberdiev" - }); - expect(requestFilter).to.be.instanceOf(UserFilter); - expect(requestFilter).to.be.eql({ - keyword: "Um" - }); - }); - }); - -}); \ No newline at end of file diff --git a/test/functional/class-validator-options.spec.ts b/test/functional/class-validator-options.spec.ts index be925cad..7394bd0a 100644 --- a/test/functional/class-validator-options.spec.ts +++ b/test/functional/class-validator-options.spec.ts @@ -1,256 +1,253 @@ import "reflect-metadata"; import {Length} from "class-validator"; import {JsonController} from "../../src/decorator/JsonController"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {defaultMetadataStorage} from "class-transformer/storage"; import {Get} from "../../src/decorator/Get"; import {QueryParam} from "../../src/decorator/QueryParam"; import {ResponseClassTransformOptions} from "../../src/decorator/ResponseClassTransformOptions"; -import {RoutingControllersOptions} from "../../src/RoutingControllersOptions"; - -const chakram = require("chakram"); -const expect = chakram.expect; - -describe("parameters auto-validation", () => { - - class UserFilter { - @Length(5, 15) - keyword: string; +import DoneCallback = jest.DoneCallback; +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import qs from "qs"; +import HttpStatusCodes from "http-status-codes"; +import {axios} from "../utilities/axios"; + +class UserFilter { + @Length(5, 15) + keyword: string; +} + +class UserModel { + id: number; + _firstName: string; + _lastName: string; + + get name(): string { + return this._firstName + " " + this._lastName; } - - class UserModel { - id: number; - _firstName: string; - _lastName: string; - - get name(): string { - return this._firstName + " " + this._lastName; +} + +afterAll(() => defaultMetadataStorage.clear()); + +describe("global validation enabled", () => { + let expressServer: HttpServer; + let requestFilter: UserFilter; + + beforeEach((done: DoneCallback) => { + requestFilter = undefined; + getMetadataArgsStorage().reset(); + + @JsonController() + class ClassTransformUserController { + @Get("/user") + getUsers(@QueryParam("filter") filter: UserFilter): any { + requestFilter = filter; + const user = new UserModel(); + user.id = 1; + user._firstName = "Umed"; + user._lastName = "Khudoiberdiev"; + return user; + } } - } - after(() => { - defaultMetadataStorage.clear(); + expressServer = createExpressServer({ + validation: true + }).listen(3001, done); }); - describe("should apply global validation enable", () => { - - let requestFilter: any; - beforeEach(() => { - requestFilter = undefined; - }); - - before(() => { - getMetadataArgsStorage().reset(); - - @JsonController() - class ClassTransformUserController { - - @Get("/user") - getUsers(@QueryParam("filter") filter: UserFilter): any { - requestFilter = filter; - const user = new UserModel(); - user.id = 1; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - return user; - } + afterEach((done: DoneCallback) => { + expressServer.close(done); + }); + it("should apply global validation enabled", () => { + expect.assertions(2); + return axios.get("/user?" + qs.stringify({ + filter: { + keyword: "Um", + __somethingPrivate: "blablabla" } - }); - - const options: RoutingControllersOptions = { - validation: true - }; - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer(options).listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer(options).listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "user?filter={\"keyword\": \"Um\", \"__somethingPrivate\": \"blablabla\"}", response => { - expect(response).to.have.status(400); - expect(requestFilter).to.be.undefined; + })).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + expect(requestFilter).toBeUndefined(); }); }); +}); - describe("should apply local validation enable", () => { - - let requestFilter: any; - beforeEach(() => { - requestFilter = undefined; - }); - - before(() => { - getMetadataArgsStorage().reset(); - - @JsonController() - class ClassTransformUserController { - - @Get("/user") - @ResponseClassTransformOptions({ excludePrefixes: ["_"] }) - getUsers(@QueryParam("filter", { validate: true }) filter: UserFilter): any { - requestFilter = filter; - const user = new UserModel(); - user.id = 1; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - return user; - } - +describe("local validation enabled", () => { + let expressServer: HttpServer; + let requestFilter: UserFilter; + + beforeEach((done: DoneCallback) => { + requestFilter = undefined; + getMetadataArgsStorage().reset(); + + @JsonController() + class ClassTransformUserController { + @Get("/user") + @ResponseClassTransformOptions({excludePrefixes: ["_"]}) + getUsers(@QueryParam("filter", {validate: true}) filter: UserFilter): any { + requestFilter = filter; + const user = new UserModel(); + user.id = 1; + user._firstName = "Umed"; + user._lastName = "Khudoiberdiev"; + return user; } - }); - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); + } - assertRequest([3001, 3002], "get", "user?filter={\"keyword\": \"Um\", \"__somethingPrivate\": \"blablabla\"}", response => { - expect(response).to.have.status(400); - expect(requestFilter).to.be.undefined; - }); + expressServer = createExpressServer().listen(3001, done); }); - describe("should apply global validation options", () => { + afterEach((done: DoneCallback) => expressServer.close(done)); - let requestFilter: any; - beforeEach(() => { - requestFilter = undefined; + it("should apply local validation enabled", () => { + expect.assertions(2); + return axios.get("/user?" + qs.stringify({ + filter: { + keyword: "Um", + __somethingPrivate: "blablabla" + } + })).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + expect(requestFilter).toBeUndefined(); }); + }); - before(() => { - getMetadataArgsStorage().reset(); - - @JsonController() - class ClassTransformUserController { - - @Get("/user") - getUsers(@QueryParam("filter") filter: UserFilter): any { - requestFilter = filter; - const user = new UserModel(); - user.id = 1; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - return user; - } +}); +describe("global validation options", () => { + let expressServer: HttpServer; + let requestFilter: any; + + beforeEach((done: DoneCallback) => { + requestFilter = undefined; + getMetadataArgsStorage().reset(); + + @JsonController() + class ClassTransformUserController { + @Get("/user") + getUsers(@QueryParam("filter") filter: UserFilter): any { + requestFilter = filter; + const user = new UserModel(); + user.id = 1; + user._firstName = "Umed"; + user._lastName = "Khudoiberdiev"; + return user; } - }); + } - const options: RoutingControllersOptions = { + expressServer = createExpressServer({ validation: { skipMissingProperties: true } - }; - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer(options).listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer(options).listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "user?filter={\"notKeyword\": \"Um\", \"__somethingPrivate\": \"blablabla\"}", response => { - expect(response).to.have.status(200); - expect(requestFilter).to.have.property("notKeyword"); - }); + }).listen(3001, done); }); - describe("should pass the valid param after validation", () => { + afterEach((done: DoneCallback) => expressServer.close(done)); - let requestFilter: any; - beforeEach(() => { - requestFilter = undefined; + it("should apply global validation options", () => { + expect.assertions(2); + return axios.get("/user?" + qs.stringify({ + filter: { + notKeyword: "Um", + __somethingPrivate: "blablabla" + } + })).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(requestFilter).toHaveProperty("notKeyword"); }); + }); +}); - before(() => { - getMetadataArgsStorage().reset(); - - @JsonController() - class UserController { - - @Get("/user") - getUsers(@QueryParam("filter") filter: UserFilter): any { - requestFilter = filter; - const user = new UserModel(); - user.id = 1; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - return user; - } - +describe("valid param after validation", () => { + let expressServer: HttpServer; + let requestFilter: UserFilter; + + beforeEach((done: DoneCallback) => { + requestFilter = undefined; + getMetadataArgsStorage().reset(); + + @JsonController() + class UserController { + @Get("/user") + getUsers(@QueryParam("filter") filter: UserFilter): any { + requestFilter = filter; + const user = new UserModel(); + user.id = 1; + user._firstName = "Umed"; + user._lastName = "Khudoiberdiev"; + return user; } - }); + } - const options: RoutingControllersOptions = { + expressServer = createExpressServer({ validation: true - }; + }).listen(3001, done); + }); - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer(options).listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer(options).listen(3002, done)); - after(done => koaApp.close(done)); + afterEach((done: DoneCallback) => expressServer.close(done)); - assertRequest([3001, 3002], "get", "user?filter={\"keyword\": \"Umedi\", \"__somethingPrivate\": \"blablabla\"}", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql({ + it("should pass the valid param after validation", () => { + expect.assertions(4); + return axios.get("/user?" + qs.stringify({ + filter: { + keyword: "Umedi", + __somethingPrivate: "blablabla" + } + })).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual({ id: 1, _firstName: "Umed", _lastName: "Khudoiberdiev" }); - expect(requestFilter).to.be.instanceOf(UserFilter); - expect(requestFilter).to.be.eql({ + expect(requestFilter).toBeInstanceOf(UserFilter); + expect(requestFilter).toEqual({ keyword: "Umedi", __somethingPrivate: "blablabla", }); }); }); +}); - describe("should contain param name on validation failed", () => { - - let requestFilter: any; - beforeEach(() => { - requestFilter = undefined; - }); - - before(() => { - getMetadataArgsStorage().reset(); - - @JsonController() - class UserController { - - @Get("/user") - getUsers(@QueryParam("filter") filter: UserFilter): any { - requestFilter = filter; - const user = new UserModel(); - user.id = 1; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - return user; - } +describe("param name on validation failed", () => { + let expressServer: HttpServer; + let requestFilter: any; + + beforeEach((done: DoneCallback) => { + requestFilter = undefined; + getMetadataArgsStorage().reset(); + + @JsonController() + class UserController { + @Get("/user") + getUsers(@QueryParam("filter") filter: UserFilter): any { + requestFilter = filter; + const user = new UserModel(); + user.id = 1; + user._firstName = "Umed"; + user._lastName = "Khudoiberdiev"; + return user; } - }); + } - const options: RoutingControllersOptions = { + expressServer = createExpressServer({ validation: true - }; - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer(options).listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer(options).listen(3002, done)); - after(done => koaApp.close(done)); + }).listen(3001, done); + }); - const invalidFilter = { - keyword: "aa" - }; + afterEach((done: DoneCallback) => expressServer.close(done)); - assertRequest([3001, 3002], "get", `user?filter=${JSON.stringify(invalidFilter)}`, response => { - expect(response).to.have.status(400); - expect(response.body.paramName).to.equal("filter"); + it("should contain param name on validation failed", () => { + expect.assertions(2); + return axios.get("/user?" + qs.stringify({ + filter: { + keyword: "aa" + } + })).catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + expect(error.response.data.paramName).toEqual("filter"); }); }); }); diff --git a/test/functional/container.spec.ts b/test/functional/container.spec.ts index d57cc02e..36c02622 100644 --- a/test/functional/container.spec.ts +++ b/test/functional/container.spec.ts @@ -1,472 +1,449 @@ import "reflect-metadata"; import {JsonController} from "../../src/decorator/JsonController"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {Container, Service} from "typedi"; -import {useContainer} from "../../src/container"; +import {useContainer} from "../../src/util/container"; import {Get} from "../../src/decorator/Get"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {Server as HttpServer} from "http"; +import DoneCallback = jest.DoneCallback; +import {AxiosResponse} from "axios"; +import HttpStatusCodes from "http-status-codes"; +import {axios} from "../utilities/axios"; + +describe("using typedi container should be possible", () => { + let expressServer: HttpServer; + + beforeEach((done: DoneCallback) => { + // reset metadata args storage + useContainer(Container); + getMetadataArgsStorage().reset(); + + @Service() + class QuestionRepository { + findQuestions(): any[] { + return [{ + id: 1, + title: "question #1" + }, { + id: 2, + title: "question #2" + }]; + } + } + + @Service() + class PostRepository { + findPosts(): any[] { + return [{ + id: 1, + title: "post #1" + }, { + id: 2, + title: "post #2" + }]; + } + } + + @Service() + @JsonController() + class TestContainerController { + constructor(private questionRepository: QuestionRepository, + private postRepository: PostRepository) { + } -describe("container", () => { + @Get("/questions") + questions(): any[] { + return this.questionRepository.findQuestions(); + } - describe("using typedi container should be possible", () => { + @Get("/posts") + posts(): any[] { + return this.postRepository.findPosts(); + } + } - before(() => { + expressServer = createExpressServer().listen(3001, done); + }); - @Service() - class QuestionRepository { + afterEach((done: DoneCallback) => { + useContainer(undefined); + expressServer.close(done); + }); - findQuestions(): any[] { - return [{ + it("typedi container", () => { + expect.assertions(4); + return Promise.all([ + axios.get("/questions") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "question #1" }, { id: 2, title: "question #2" - }]; - } - - } - - @Service() - class PostRepository { - - findPosts(): any[] { - return [{ + }]); + }), + axios.get("/posts") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "post #1" }, { id: 2, title: "post #2" - }]; - } - - } - - // reset metadata args storage - useContainer(Container); - getMetadataArgsStorage().reset(); + }]); + }) + ]); + }); +}); - @Service() - @JsonController() - class TestContainerController { +describe("using custom container should be possible", () => { + let expressServer: HttpServer; - constructor(private questionRepository: QuestionRepository, - private postRepository: PostRepository) { - } + beforeEach((done: DoneCallback) => { + const fakeContainer = { + services: [] as any, - @Get("/questions") - questions(): any[] { - return this.questionRepository.findQuestions(); - } - - @Get("/posts") - posts(): any[] { - return this.postRepository.findPosts(); + get(service: any) { + if (!this.services[service.name]) { + this.services[service.name] = new service(); } + return this.services[service.name]; } - }); - - after(() => { - useContainer(undefined); - }); - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "questions", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "question #1" - }, { - id: 2, - title: "question #2" - }]); - }); - - assertRequest([3001, 3002], "get", "posts", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "post #1" - }, { - id: 2, - title: "post #2" - }]); - }); - }); - - describe("using custom container should be possible", () => { + }; + + class QuestionRepository { + findQuestions(): any[] { + return [{ + id: 1, + title: "question #1" + }, { + id: 2, + title: "question #2" + }]; + } + } + + class PostRepository { + findPosts(): any[] { + return [{ + id: 1, + title: "post #1" + }, { + id: 2, + title: "post #2" + }]; + } + } - before(() => { + // reset metadata args storage + useContainer(fakeContainer); + getMetadataArgsStorage().reset(); - const fakeContainer = { - services: [] as any, + @JsonController() + class TestContainerController { + constructor(private questionRepository: QuestionRepository, + private postRepository: PostRepository) { + } - get(service: any) { - if (!this.services[service.name]) { - this.services[service.name] = new service(); - } + @Get("/questions") + questions(): any[] { + return this.questionRepository.findQuestions(); + } - return this.services[service.name]; - } - }; + @Get("/posts") + posts(): any[] { + return this.postRepository.findPosts(); + } + } + const postRepository = new PostRepository(); + const questionRepository = new QuestionRepository(); + fakeContainer.services["TestContainerController"] = new TestContainerController(questionRepository, postRepository); + expressServer = createExpressServer().listen(3001, done); + }); - class QuestionRepository { + afterEach((done: DoneCallback) => { + useContainer(undefined); + expressServer.close(done); + }); - findQuestions(): any[] { - return [{ + it("custom container", () => { + expect.assertions(4); + return Promise.all([ + axios.get("/questions") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "question #1" }, { id: 2, title: "question #2" - }]; - } - - } - - class PostRepository { - - findPosts(): any[] { - return [{ + }]); + }), + axios.get("/posts") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "post #1" }, { id: 2, title: "post #2" - }]; - } + }]); + }) + ]); + }); +}); - } +describe("using custom container with fallback should be possible", () => { + let expressServer: HttpServer; - // reset metadata args storage - useContainer(fakeContainer); - getMetadataArgsStorage().reset(); + beforeEach((done: DoneCallback) => { + const fakeContainer = { + services: [] as any, - @JsonController() - class TestContainerController { + get(service: any): any { + return this.services[service.name]; + } + }; + + class QuestionRepository { + findQuestions(): any[] { + return [{ + id: 1, + title: "question #1" + }, { + id: 2, + title: "question #2" + }]; + } + } + + class PostRepository { + findPosts(): any[] { + return [{ + id: 1, + title: "post #1" + }, { + id: 2, + title: "post #2" + }]; + } + } - constructor(private questionRepository: QuestionRepository, - private postRepository: PostRepository) { - } + // reset metadata args storage + useContainer(fakeContainer, {fallback: true}); + getMetadataArgsStorage().reset(); - @Get("/questions") - questions(): any[] { - return this.questionRepository.findQuestions(); - } + @JsonController() + class TestContainerController { + constructor(private questionRepository: QuestionRepository, + private postRepository: PostRepository) { + } - @Get("/posts") - posts(): any[] { - return this.postRepository.findPosts(); - } + @Get("/questions") + questions(): any[] { + return this.questionRepository.findQuestions(); + } + @Get("/posts") + posts(): any[] { + return this.postRepository.findPosts(); } + } + + @JsonController() + class SecondTestContainerController { + @Get("/photos") + photos(): any[] { + return [{ + id: 1, + title: "photo #1" + }, { + id: 2, + title: "photo #2" + }]; + } + } - const postRepository = new PostRepository(); - const questionRepository = new QuestionRepository(); - fakeContainer.services["TestContainerController"] = new TestContainerController(questionRepository, postRepository); - }); - - after(() => { - useContainer(undefined); - }); - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "questions", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "question #1" - }, { - id: 2, - title: "question #2" - }]); - }); - - assertRequest([3001, 3002], "get", "posts", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "post #1" - }, { - id: 2, - title: "post #2" - }]); - }); + const postRepository = new PostRepository(); + const questionRepository = new QuestionRepository(); + fakeContainer.services["TestContainerController"] = new TestContainerController(questionRepository, postRepository); + expressServer = createExpressServer().listen(3001, done); }); - describe("using custom container with fallback should be possible", () => { - - before(() => { - - const fakeContainer = { - services: [] as any, - - get(service: any): any { - return this.services[service.name]; - } - }; - - - class QuestionRepository { + afterEach((done: DoneCallback) => { + useContainer(undefined); + expressServer.close(done); + }); - findQuestions(): any[] { - return [{ + it("custom container with fallback", () => { + expect.assertions(6); + return Promise.all([ + axios.get("/questions") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "question #1" }, { id: 2, title: "question #2" - }]; - } - - } - - class PostRepository { - - findPosts(): any[] { - return [{ + }]); + }), + axios.get("/posts") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "post #1" }, { id: 2, title: "post #2" - }]; - } - - } - - // reset metadata args storage - useContainer(fakeContainer, { fallback: true }); - getMetadataArgsStorage().reset(); - - @JsonController() - class TestContainerController { - - constructor(private questionRepository: QuestionRepository, - private postRepository: PostRepository) { - } - - @Get("/questions") - questions(): any[] { - return this.questionRepository.findQuestions(); - } - - @Get("/posts") - posts(): any[] { - return this.postRepository.findPosts(); - } - - } - - @JsonController() - class SecondTestContainerController { - - @Get("/photos") - photos(): any[] { - return [{ + }]); + }), + axios.get("/photos") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "photo #1" }, { id: 2, title: "photo #2" - }]; - } + }]); + }) + ]); + }); +}); - } +describe("using custom container with fallback and fallback on throw error should be possible", () => { + let expressServer: HttpServer; - const postRepository = new PostRepository(); - const questionRepository = new QuestionRepository(); - fakeContainer.services["TestContainerController"] = new TestContainerController(questionRepository, postRepository); - }); - - after(() => { - useContainer(undefined); - }); - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "questions", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "question #1" - }, { - id: 2, - title: "question #2" - }]); - }); - - assertRequest([3001, 3002], "get", "posts", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "post #1" - }, { - id: 2, - title: "post #2" - }]); - }); - - assertRequest([3001, 3002], "get", "photos", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "photo #1" - }, { - id: 2, - title: "photo #2" - }]); - }); - }); + beforeEach((done: DoneCallback) => { + const fakeContainer = { + services: [] as any, - describe("using custom container with fallback and fallback on throw error should be possible", () => { + get(service: any): any { + if (!this.services[service.name]) + throw new Error(`Provider was not found for ${service.name}`); - before(() => { + return this.services[service.name]; + } + }; + + class QuestionRepository { + findQuestions(): any[] { + return [{ + id: 1, + title: "question #1" + }, { + id: 2, + title: "question #2" + }]; + } + } + + class PostRepository { + findPosts(): any[] { + return [{ + id: 1, + title: "post #1" + }, { + id: 2, + title: "post #2" + }]; + } + } - const fakeContainer = { - services: [] as any, + // reset metadata args storage + useContainer(fakeContainer, {fallback: true, fallbackOnErrors: true}); + getMetadataArgsStorage().reset(); - get(service: any): any { - if (!this.services[service.name]) - throw new Error(`Provider was not found for ${service.name}`); + @JsonController() + class TestContainerController { + constructor(private questionRepository: QuestionRepository, + private postRepository: PostRepository) { + } - return this.services[service.name]; - } - }; + @Get("/questions") + questions(): any[] { + return this.questionRepository.findQuestions(); + } + @Get("/posts") + posts(): any[] { + return this.postRepository.findPosts(); + } + } + + @JsonController() + class SecondTestContainerController { + @Get("/photos") + photos(): any[] { + return [{ + id: 1, + title: "photo #1" + }, { + id: 2, + title: "photo #2" + }]; + } + } + + const postRepository = new PostRepository(); + const questionRepository = new QuestionRepository(); + fakeContainer.services["TestContainerController"] = new TestContainerController(questionRepository, postRepository); + expressServer = createExpressServer().listen(3001, done); + }); - class QuestionRepository { + afterEach((done: DoneCallback) => { + useContainer(undefined); + expressServer.close(done); + }); - findQuestions(): any[] { - return [{ + it("custom container with fallback and fallback on throw error", () => { + expect.assertions(6); + return Promise.all([ + axios.get("/questions") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "question #1" }, { id: 2, title: "question #2" - }]; - } - - } - - class PostRepository { - - findPosts(): any[] { - return [{ + }]); + }), + axios.get("/posts") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "post #1" }, { id: 2, title: "post #2" - }]; - } - - } - - // reset metadata args storage - useContainer(fakeContainer, { fallback: true, fallbackOnErrors: true }); - getMetadataArgsStorage().reset(); - - @JsonController() - class TestContainerController { - - constructor(private questionRepository: QuestionRepository, - private postRepository: PostRepository) { - } - - @Get("/questions") - questions(): any[] { - return this.questionRepository.findQuestions(); - } - - @Get("/posts") - posts(): any[] { - return this.postRepository.findPosts(); - } - - } - - @JsonController() - class SecondTestContainerController { - - @Get("/photos") - photos(): any[] { - return [{ + }]); + }), + axios.get("/photos") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{ id: 1, title: "photo #1" }, { id: 2, title: "photo #2" - }]; - } - - } - - const postRepository = new PostRepository(); - const questionRepository = new QuestionRepository(); - fakeContainer.services["TestContainerController"] = new TestContainerController(questionRepository, postRepository); - }); - - after(() => { - useContainer(undefined); - }); - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "questions", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "question #1" - }, { - id: 2, - title: "question #2" - }]); - }); - - assertRequest([3001, 3002], "get", "posts", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "post #1" - }, { - id: 2, - title: "post #2" - }]); - }); - - assertRequest([3001, 3002], "get", "photos", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql([{ - id: 1, - title: "photo #1" - }, { - id: 2, - title: "photo #2" - }]); - }); + }]); + }) + ]); }); +}); -}); \ No newline at end of file diff --git a/test/functional/controller-base-routes.spec.ts b/test/functional/controller-base-routes.spec.ts index 8e7e2359..a2653d34 100644 --- a/test/functional/controller-base-routes.spec.ts +++ b/test/functional/controller-base-routes.spec.ts @@ -1,13 +1,17 @@ import "reflect-metadata"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {Controller} from "../../src/decorator/Controller"; import {Get} from "../../src/decorator/Get"; -const expect = require("chakram").expect; +import DoneCallback = jest.DoneCallback; +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import {axios} from "../utilities/axios"; describe("controller > base routes functionality", () => { - before(() => { - // reset metadata args storage + let expressServer: HttpServer; + + beforeEach((done: DoneCallback) => { getMetadataArgsStorage().reset(); @Controller("/posts") @@ -16,68 +20,79 @@ describe("controller > base routes functionality", () => { getAll() { return "All posts"; } + @Get("/:id(\\d+)") getUserById() { return "One post"; } + @Get(/\/categories\/(\d+)/) getCategoryById() { return "One post category"; } + @Get("/:postId(\\d+)/users/:userId(\\d+)") getPostById() { return "One user"; } } + expressServer = createExpressServer().listen(3001, done); }); - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); + afterEach((done: DoneCallback) => expressServer.close(done)); - describe("get should respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "get", "posts", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("All posts"); - }); + it("get should respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.get("/posts") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("All posts"); + }); }); - describe("get should respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "get", "posts/1", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("One post"); - }); + it("get should respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.get("/posts/1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("One post"); + }); }); - describe("get should respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "get", "posts/1/users/2", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("One user"); - }); + it("get should respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.get("posts/1/users/2") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("One user"); + }); }); - describe("wrong route should respond with 404 error", () => { - assertRequest([3001, 3002], "get", "1/users/1", response => { - expect(response).to.have.status(404); - }); + it("wrong route should respond with 404 error", () => { + expect.assertions(1); + return axios.get("/1/users/1") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + }); }); - describe("wrong route should respond with 404 error", () => { - assertRequest([3001, 3002], "get", "categories/1", response => { - expect(response).to.have.status(404); - }); + it("wrong route should respond with 404 error", () => { + expect.assertions(1); + return axios.get("/categories/1") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + }); }); - describe("wrong route should respond with 404 error", () => { - assertRequest([3001, 3002], "get", "users/1", response => { - expect(response).to.have.status(404); - }); + it("wrong route should respond with 404 error", () => { + expect.assertions(1); + return axios.get("/users/1") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + }); }); - -}); \ No newline at end of file +}); diff --git a/test/functional/controller-methods.spec.ts b/test/functional/controller-methods.spec.ts index da682f7f..c1cfea11 100644 --- a/test/functional/controller-methods.spec.ts +++ b/test/functional/controller-methods.spec.ts @@ -10,20 +10,17 @@ import {Put} from "../../src/decorator/Put"; import {ContentType} from "../../src/decorator/ContentType"; import {JsonController} from "../../src/decorator/JsonController"; import {UnauthorizedError} from "../../src/http-error/UnauthorizedError"; -import { - createExpressServer, - createKoaServer, - getMetadataArgsStorage, -} from "../../src/index"; -import {assertRequest} from "./test-utils"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import {axios} from "../utilities/axios"; describe("controller methods", () => { + let expressServer: HttpServer; - before(() => { - - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @Controller() @@ -32,46 +29,57 @@ describe("controller methods", () => { getAll() { return "All users"; } + @Post("/users") post() { return "Posting user"; } + @Put("/users") put() { return "Putting user"; } + @Patch("/users") patch() { return "Patching user"; } + @Delete("/users") delete() { return "Removing user"; } + @Head("/users") head() { return "Removing user"; } + @Method("post", "/categories") postCategories() { return "Posting categories"; } + @Method("delete", "/categories") getCategories() { return "Get categories"; } + @Get("/users/:id") getUserById() { return "One user"; } + @Get(/\/categories\/[\d+]/) getCategoryById() { return "One category"; } + @Get("/posts/:id(\\d+)") getPostById() { return "One post"; } + @Get("/posts-from-db") getPostFromDb() { return new Promise((ok, fail) => { @@ -80,6 +88,7 @@ describe("controller methods", () => { }, 500); }); } + @Get("/posts-from-failed-db") getPostFromFailedDb() { return new Promise((ok, fail) => { @@ -96,6 +105,7 @@ describe("controller methods", () => { returnUndefined(): undefined { return undefined; } + @Get("/null") returnNull(): null { return null; @@ -108,6 +118,7 @@ describe("controller methods", () => { returnUndefined(): undefined { return undefined; } + @Get("/null") returnNull(): null { return null; @@ -140,201 +151,231 @@ describe("controller methods", () => { } } + expressServer = createExpressServer().listen(3001, done); }); - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - describe("get should respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "get", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("All users"); - }); + afterAll((done: DoneCallback) => expressServer.close(done)); + + it("get should respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.get("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("All users"); + }); }); - describe("post respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "post", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("Posting user"); - }); + it("post respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.post("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("Posting user"); + }); }); - - describe("put respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "put", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("Putting user"); - }); + + it("put respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.put("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("Putting user"); + }); }); - - describe("patch respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "patch", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("Patching user"); - }); + + it("patch respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.patch("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("Patching user"); + }); }); - - describe("delete respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "delete", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("Removing user"); - }); + + it("delete respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.delete("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("Removing user"); + }); }); - describe("head respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "head", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.undefined; - }); + it("head respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.head("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual(""); + }); }); - describe("custom method (post) respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "post", "categories", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("Posting categories"); - }); + it("custom method (post) respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.post("/categories") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("Posting categories"); + }); }); - describe("custom method (delete) respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "delete", "categories", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("Get categories"); - }); + it("custom method (delete) respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.delete("/categories") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("Get categories"); + }); }); - describe("route should work with parameter", () => { - assertRequest([3001, 3002], "get", "users/umed", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("One user"); - }); + it("route should work with parameter", () => { + expect.assertions(3); + return axios.get("/users/umed") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("One user"); + }); }); - describe("route should work with regexp parameter", () => { - assertRequest([3001, 3002], "get", "categories/1", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("One category"); - }); + it("route should work with regexp parameter", () => { + return axios.get("/categories/1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("One category"); + }); }); - describe("should respond with 404 when regexp does not match", () => { - assertRequest([3001, 3002], "get", "categories/umed", response => { - expect(response).to.have.status(404); - }); + it("should respond with 404 when regexp does not match", () => { + expect.assertions(1); + return axios.get("/categories/umed") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + }); }); - describe("route should work with string regexp parameter", () => { - assertRequest([3001, 3002], "get", "posts/1", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("One post"); - }); + it("route should work with string regexp parameter", () => { + expect.assertions(3); + return axios.get("/posts/1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("One post"); + }); }); - describe("should respond with 404 when regexp does not match", () => { - assertRequest([3001, 3002], "get", "posts/U", response => { - expect(response).to.have.status(404); - }); + it("should respond with 404 when regexp does not match", () => { + expect.assertions(1); + return axios.get("/posts/U") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + }); }); - describe("should return result from a promise", () => { - assertRequest([3001, 3002], "get", "posts-from-db", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("resolved after half second"); - }); + it("should return result from a promise", () => { + expect.assertions(3); + return axios.get("/posts-from-db") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("resolved after half second"); + }); }); - describe("should respond with 500 if promise failed", () => { - assertRequest([3001, 3002], "get", "posts-from-failed-db", response => { - expect(response).to.have.status(500); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("cannot connect to a database"); - }); + it("should respond with 500 if promise failed", () => { + expect.assertions(3); + return axios.get("/posts-from-failed-db") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); + expect(error.response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(error.response.data).toEqual("cannot connect to a database"); + }); }); - describe("should respond with 204 No Content when null returned in action", () => { - assertRequest([3001, 3002], "get", "return/normal/null", response => { - expect(response).to.have.status(204); - expect(response).to.not.have.header("content-type"); - expect(response.body).to.not.exist; - }); - assertRequest([3001, 3002], "get", "return/json/null", response => { - expect(response).to.have.status(204); - expect(response).to.not.have.header("content-type"); - expect(response.body).to.not.exist; - }); + it("should respond with 204 No Content when null returned in action", () => { + expect.assertions(6); + return Promise.all([ + axios.get("/return/normal/null") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.NO_CONTENT); + expect(response.headers["content-type"]).toBeUndefined(); + expect(response.data).toEqual(""); + }), + axios.get("/return/json/null") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.NO_CONTENT); + expect(response.headers["content-type"]).toBeUndefined(); + expect(response.data).toEqual(""); + }) + ]); }); - describe("should respond with 404 Not Found text when undefined returned in action", () => { - assertRequest([3001, 3002], "get", "return/normal/undefined", response => { - expect(response).to.have.status(404); - expect(response).to.have.header("content-type", (contentType: string) => { - expect(contentType).to.match(/text/); + it("should respond with 404 Not Found text when undefined returned in action", () => { + expect.assertions(2); + return axios.get("/return/normal/undefined") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + expect(error.response.headers["content-type"]).toEqual("text/html; charset=utf-8"); }); - }); }); - describe("should respond with 404 Not Found JSON when undefined returned in action", () => { - assertRequest([3001, 3002], "get", "return/json/undefined", response => { - expect(response).to.have.status(404); - expect(response).to.have.header("content-type", (contentType: string) => { - expect(contentType).to.match(/application\/json/); + it("should respond with 404 Not Found JSON when undefined returned in action", () => { + expect.assertions(2); + return axios.get("/return/json/undefined") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + expect(error.response.headers["content-type"]).toEqual("application/json; charset=utf-8"); }); - }); }); - describe("should respond with 200 and text/html even in json controller's method", () => { - assertRequest([3001, 3002], "get", "json-controller/text-html", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", (contentType: string) => { - expect(contentType).to.match(/text\/html/); + it("should respond with 200 and text/html even in json controller's method", () => { + expect.assertions(3); + return axios.get("/json-controller/text-html") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("Test"); }); - expect(response.body).to.equals("Test"); - }); }); - describe("should respond with 200 and text/plain even in json controller's method", () => { - assertRequest([3001, 3002], "get", "json-controller/text-plain", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", (contentType: string) => { - expect(contentType).to.match(/text\/plain/); + it("should respond with 200 and text/plain even in json controller's method", () => { + expect.assertions(3); + return axios.get("/json-controller/text-plain") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/plain; charset=utf-8"); + expect(response.data).toEqual("Test"); }); - expect(response.body).to.equals("Test"); - }); }); - describe("should respond with 401 and text/html when UnauthorizedError throwed even in json controller's method", () => { - assertRequest([3001, 3002], "get", "json-controller/text-plain-error", response => { - expect(response).to.have.status(401); - expect(response).to.have.header("content-type", (contentType: string) => { - expect(contentType).to.match(/text\/plain/); + it("should respond with 401 and text/html when UnauthorizedError throwed even in json controller's method", () => { + expect.assertions(4); + return axios.get("/json-controller/text-plain-error") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.UNAUTHORIZED); + expect(error.response.headers["content-type"]).toEqual("text/plain; charset=utf-8"); + expect(typeof error.response.data).toEqual("string"); + expect(error.response.data).toMatch(/UnauthorizedError.HttpError/); }); - expect(typeof response.body).to.equals("string"); - expect(response.body).to.match(/UnauthorizedError.HttpError/); - }); }); - describe("should respond with 401 and aplication/json when UnauthorizedError throwed in standard json controller's method", () => { - assertRequest([3001, 3002], "get", "json-controller/json-error", response => { - expect(response).to.have.status(401); - expect(response).to.have.header("content-type", (contentType: string) => { - expect(contentType).to.match(/application\/json/); + it("should respond with 401 and aplication/json when UnauthorizedError is thrown in standard json controller's method", () => { + expect.assertions(4); + return axios.get("/json-controller/json-error") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.UNAUTHORIZED); + expect(error.response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(typeof error.response.data).toEqual("object"); + expect(error.response.data.name).toEqual("UnauthorizedError"); }); - expect(typeof response.body).to.equals("object"); - expect(response.body.name).to.equals("UnauthorizedError"); - }); }); - -}); \ No newline at end of file +}); diff --git a/test/functional/defaults.spec.ts b/test/functional/defaults.spec.ts index 0205625e..d85c38dc 100644 --- a/test/functional/defaults.spec.ts +++ b/test/functional/defaults.spec.ts @@ -1,112 +1,119 @@ import "reflect-metadata"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {Controller} from "../../src/decorator/Controller"; import {Get} from "../../src/decorator/Get"; import {QueryParam} from "../../src/decorator/QueryParam"; import {OnUndefined} from "../../src/decorator/OnUndefined"; -import {assertRequest} from "./test-utils"; - -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import {axios} from "../utilities/axios"; describe("defaults", () => { + let expressServer: HttpServer; + const defaultUndefinedResultCode = 204; + const defaultNullResultCode = 404; - before(() => { - - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @Controller() class ExpressController { - @Get("/voidfunc") - voidfunc() { } + voidFunc() { + // Empty + } @Get("/promisevoidfunc") - promisevoidfunc() { + promiseVoidFunc() { return Promise.resolve(); } @Get("/paramfunc") - paramfunc(@QueryParam("x") x: number) { - return { foo: "bar" }; + paramFunc(@QueryParam("x") x: number) { + return { + foo: "bar" + }; } @Get("/nullfunc") - nullfunc(): string { + nullFunc(): string { return null; } @Get("/overridefunc") - @OnUndefined(404) - overridefunc() { } + @OnUndefined(HttpStatusCodes.NOT_ACCEPTABLE) + overrideFunc() { + // Empty + } @Get("/overrideparamfunc") - overrideparamfunc(@QueryParam("x", { required: false }) x: number) { - return { foo: "bar" }; + overrideParamFunc(@QueryParam("x", {required: false}) x: number) { + return { + foo: "bar" + }; } } - }); - let defaultUndefinedResultCode = 204; - let defaultNullResultCode = 404; - let expressApp: any; - let kuaApp: any; - before(done => expressApp = createExpressServer({ - defaults: { - nullResultCode: defaultNullResultCode, - undefinedResultCode: defaultUndefinedResultCode, - paramOptions: { - required: true + expressServer = createExpressServer({ + defaults: { + nullResultCode: defaultNullResultCode, + undefinedResultCode: defaultUndefinedResultCode, + paramOptions: { + required: true + } } - } - }).listen(3001, done)); - before(done => kuaApp = createKoaServer({ - defaults: { - nullResultCode: defaultNullResultCode, - undefinedResultCode: defaultUndefinedResultCode, - paramOptions: { - required: true - } - } - }).listen(3002, done)); - after(done => expressApp.close(done)); - after(done => kuaApp.close(done)); + }).listen(3001, done); + }); + + afterAll((done: DoneCallback) => expressServer.close(done)); it("should return undefinedResultCode from defaults config for void function", () => { - assertRequest([3001, 3002], "get", "voidfunc", res => { - expect(res).to.have.status(defaultUndefinedResultCode); - }); + expect.assertions(1); + return axios.get("/voidfunc") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(defaultUndefinedResultCode); + }); }); it("should return undefinedResultCode from defaults config for promise void function", () => { - assertRequest([3001, 3002], "get", "promisevoidfunc", res => { - expect(res).to.have.status(defaultUndefinedResultCode); - }); + expect.assertions(1); + return axios.get("/promisevoidfunc") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(defaultUndefinedResultCode); + }); }); it("should return 400 from required paramOptions", () => { - assertRequest([3001, 3002], "get", "paramfunc", res => { - expect(res).to.have.status(400); - }); + expect.assertions(1); + return axios.get("/paramfunc") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.BAD_REQUEST); + }); }); it("should return nullResultCode from defaults config", () => { - assertRequest([3001, 3002], "get", "nullfunc", res => { - expect(res).to.have.status(defaultNullResultCode); - }); + expect.assertions(1); + return axios.get("/nullfunc") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(defaultNullResultCode); + }); }); it("should return status code from OnUndefined annotation", () => { - assertRequest([3001, 3002], "get", "overridefunc", res => { - expect(res).to.have.status(404); - }); + expect.assertions(1); + return axios.get("/overridefunc") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_ACCEPTABLE); + }); }); it("should mark arg optional from QueryParam annotation", () => { - assertRequest([3001, 3002], "get", "overrideparamfunc", res => { - expect(res).to.have.status(200); - }); + expect.assertions(1); + return axios.get("/overrideparamfunc") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + }); }); - -}); \ No newline at end of file +}); diff --git a/test/functional/error-subclasses.spec.ts b/test/functional/error-subclasses.spec.ts index 7e648698..30c925d5 100644 --- a/test/functional/error-subclasses.spec.ts +++ b/test/functional/error-subclasses.spec.ts @@ -1,26 +1,23 @@ -import {expect} from "chai"; - import {HttpError} from "../../src/http-error/HttpError"; import {BadRequestError} from "../../src/http-error/BadRequestError"; -describe("using Error subclasses should be possible,", () => { - describe("HttpError", () => { - it("should be instance of HttpError and Error", () => { - const error = new HttpError(418, "Error message"); - expect(error.httpCode).to.equals(418); - expect(error.message).to.equals("Error message"); - expect(error).to.be.instanceOf(HttpError); - expect(error).to.be.instanceOf(Error); - }); +describe("HttpError", () => { + it("should be instance of HttpError and Error", () => { + const error = new HttpError(418, "Error message"); + expect(error.httpCode).toEqual(418); + expect(error.message).toEqual("Error message"); + expect(error).toBeInstanceOf(HttpError); + expect(error).toBeInstanceOf(Error); }); - describe("BadRequestError", () => { - it("should be instance of BadRequestError, HttpError and Error", () => { - const error = new BadRequestError("Error message"); - expect(error.httpCode).to.equals(400); - expect(error.message).to.equals("Error message"); - expect(error).to.be.instanceOf(BadRequestError); - expect(error).to.be.instanceOf(HttpError); - expect(error).to.be.instanceOf(Error); - }); +}); + +describe("BadRequestError", () => { + it("should be instance of BadRequestError, HttpError and Error", () => { + const error = new BadRequestError("Error message"); + expect(error.httpCode).toEqual(400); + expect(error.message).toEqual("Error message"); + expect(error).toBeInstanceOf(BadRequestError); + expect(error).toBeInstanceOf(HttpError); + expect(error).toBeInstanceOf(Error); }); }); diff --git a/test/functional/express-custom-error-handling.spec.ts b/test/functional/express-custom-error-handling.spec.ts index 7b69551e..f387f9db 100644 --- a/test/functional/express-custom-error-handling.spec.ts +++ b/test/functional/express-custom-error-handling.spec.ts @@ -1,32 +1,33 @@ import "reflect-metadata"; import {JsonController} from "../../src/decorator/JsonController"; import {Get} from "../../src/decorator/Get"; -import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; +import {createExpressServer, getMetadataArgsStorage, HttpError} from "../../src/index"; import {ExpressErrorMiddlewareInterface} from "../../src/driver/express/ExpressErrorMiddlewareInterface"; import {NotFoundError} from "../../src/http-error/NotFoundError"; import {Middleware} from "../../src/decorator/Middleware"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import express from "express"; +import {axios} from "../utilities/axios"; describe("custom express error handling", () => { - let errorHandlerCalled: boolean; + let expressServer: HttpServer; beforeEach(() => { - errorHandlerCalled = undefined; + errorHandlerCalled = false; }); - - before(() => { - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @Middleware({ type: "after" }) class CustomErrorHandler implements ExpressErrorMiddlewareInterface { - error(error: any, req: any, res: any, next: any) { + error(error: HttpError, request: express.Request, response: express.Response, next: express.NextFunction): any { errorHandlerCalled = true; - - res.status(error.httpCode).send(error.message); + response.status(error.httpCode).send(error.message); } } @@ -45,37 +46,30 @@ describe("custom express error handling", () => { throw new NotFoundError("Videos were not found."); } } + + expressServer = createExpressServer({ + defaultErrorHandler: false + }).listen(3001, done); }); - let app: any; - before(done => app = createExpressServer({defaultErrorHandler: false}).listen(3001, done)); - after(done => app.close(done)); + afterAll((done: DoneCallback) => expressServer.close(done)); it("should not call global error handler middleware if there was no errors", () => { - return chakram - .get("http://127.0.0.1:3001/blogs") - .then((response: any) => { - expect(errorHandlerCalled).to.be.empty; - expect(response).to.have.status(200); + expect.assertions(2); + return axios.get("/blogs") + .then((response: AxiosResponse) => { + expect(errorHandlerCalled).toBeFalsy(); + expect(response.status).toEqual(HttpStatusCodes.OK); }); }); it("should call global error handler middleware", () => { - return chakram - .get("http://127.0.0.1:3001/videos") - .then((response: any) => { - expect(errorHandlerCalled).to.be.true; - expect(response).to.have.status(404); - }); - }); - - it("should be able to send response", () => { - return chakram - .get("http://127.0.0.1:3001/videos") - .then((response: any) => { - expect(errorHandlerCalled).to.be.true; - expect(response).to.have.status(404); - expect(response.body).to.equals("Videos were not found."); + expect.assertions(3); + return axios.get("/videos") + .catch((error: AxiosError) => { + expect(errorHandlerCalled).toBeTruthy(); + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + expect(error.response.data).toEqual("Videos were not found.") }); }); -}); \ No newline at end of file +}); diff --git a/test/functional/express-error-handling.spec.ts b/test/functional/express-error-handling.spec.ts index caac64d4..415ff087 100644 --- a/test/functional/express-error-handling.spec.ts +++ b/test/functional/express-error-handling.spec.ts @@ -7,52 +7,48 @@ import {UseAfter} from "../../src/decorator/UseAfter"; import {ExpressErrorMiddlewareInterface} from "../../src/driver/express/ExpressErrorMiddlewareInterface"; import {NotFoundError} from "../../src/http-error/NotFoundError"; import {HttpError} from "../../src/http-error/HttpError"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import express from "express"; +import {axios} from "../utilities/axios"; describe("express error handling", () => { - - let errorHandlerCalled: boolean, - errorHandledSpecifically: boolean; + let expressServer: HttpServer; + let errorHandlerCalled: boolean; + let errorHandledSpecifically: boolean; beforeEach(() => { errorHandlerCalled = undefined; errorHandledSpecifically = undefined; }); - before(() => { - - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @Middleware({ type: "after" }) class AllErrorsHandler implements ExpressErrorMiddlewareInterface { - - error(error: any, request: any, response: any, next?: Function): any { + error(error: HttpError, request: express.Request, response: express.Response, next: express.NextFunction): any { errorHandlerCalled = true; // ERROR HANDLED GLOBALLY next(error); } - } class SpecificErrorHandler implements ExpressErrorMiddlewareInterface { - - error(error: any, request: any, response: any, next?: Function): any { + error(error: HttpError, request: express.Request, response: express.Response, next: express.NextFunction): any { errorHandledSpecifically = true; // ERROR HANDLED SPECIFICALLY next(error); } - } class SoftErrorHandler implements ExpressErrorMiddlewareInterface { - - error(error: any, request: any, response: any, next?: Function): any { + error(error: HttpError, request: express.Request, response: express.Response, next: express.NextFunction): any { // ERROR WAS IGNORED next(); } - } class ToJsonError extends HttpError { @@ -76,7 +72,6 @@ describe("express error handling", () => { @JsonController() class ExpressErrorHandlerController { - @Get("/blogs") blogs() { return { @@ -108,11 +103,6 @@ describe("express error handling", () => { } @Get("/photos") - /*@UseAfter(function (error: any, request: any, response: any, next: Function) { - useAfter = true; - useCallOrder = "setFromUseAfter"; - next(); - })*/ photos() { return "1234"; } @@ -121,73 +111,70 @@ describe("express error handling", () => { stories() { throw new ToJsonError(503, "sorry, try it again later", "impatient user"); } - } + + expressServer = createExpressServer().listen(3001, done); }); - let app: any; - before(done => app = createExpressServer().listen(3001, done)); - after(done => app.close(done)); + afterAll((done: DoneCallback) => expressServer.close(done)); it("should not call global error handler middleware if there was no errors", () => { - return chakram - .get("http://127.0.0.1:3001/blogs") - .then((response: any) => { - expect(errorHandlerCalled).to.be.empty; - expect(errorHandledSpecifically).to.be.empty; - expect(response).to.have.status(200); + expect.assertions(2); + return axios.get("/blogs") + .then((response: AxiosResponse) => { + expect(errorHandlerCalled).toBeFalsy(); + expect(response.status).toEqual(HttpStatusCodes.OK); }); }); it("should call global error handler middleware", () => { - return chakram - .get("http://127.0.0.1:3001/posts") - .then((response: any) => { - expect(errorHandlerCalled).to.be.true; - expect(errorHandledSpecifically).to.be.empty; - expect(response).to.have.status(500); + expect.assertions(3); + return axios.get("/posts") + .catch((error: AxiosError) => { + expect(errorHandlerCalled).toBeTruthy(); + expect(errorHandledSpecifically).toBeFalsy(); + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); }); }); it("should call global error handler middleware", () => { - return chakram - .get("http://127.0.0.1:3001/videos") - .then((response: any) => { - expect(errorHandlerCalled).to.be.true; - expect(errorHandledSpecifically).to.be.empty; - expect(response).to.have.status(404); + expect.assertions(3); + return axios.get("/videos") + .catch((error: AxiosError) => { + expect(errorHandlerCalled).toBeTruthy(); + expect(errorHandledSpecifically).toBeFalsy(); + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); }); }); it("should call error handler middleware if used", () => { - return chakram - .get("http://127.0.0.1:3001/questions") - .then((response: any) => { - expect(errorHandlerCalled).to.be.true; - expect(errorHandledSpecifically).to.be.true; - expect(response).to.have.status(500); + expect.assertions(3); + return axios.get("/questions") + .catch((error: AxiosError) => { + expect(errorHandlerCalled).toBeTruthy(); + expect(errorHandledSpecifically).toBeTruthy(); + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); }); }); it("should not execute next middleware if soft error handled specifically and stopped error bubbling", () => { - return chakram - .get("http://127.0.0.1:3001/files") - .then((response: any) => { - expect(errorHandlerCalled).to.be.empty; - expect(errorHandledSpecifically).to.be.empty; - expect(response).to.have.status(500); + expect.assertions(3); + return axios.get("/files") + .catch((error: AxiosError) => { + expect(errorHandlerCalled).toBeFalsy(); + expect(errorHandledSpecifically).toBeFalsy(); + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); }); }); it("should process JsonErrors by their toJSON method if it exists", () => { - return chakram - .get("http://127.0.0.1:3001/stories") - .then((response: any) => { - expect(response).to.have.status(503); - expect(response.body).to.have.property("status").and.equals(503); - expect(response.body).to.have.property("publicData").and.equals("sorry, try it again later (503)"); - expect(response.body).to.not.have.property("secretData"); + expect.assertions(4); + return axios.get("/stories") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.SERVICE_UNAVAILABLE); + expect(error.response.data.status).toEqual(HttpStatusCodes.SERVICE_UNAVAILABLE); + expect(error.response.data.publicData).toEqual("sorry, try it again later (503)"); + expect(error.response.data.secretData).toBeUndefined(); }); }); - -}); \ No newline at end of file +}); diff --git a/test/functional/express-global-before-error-handling.spec.ts b/test/functional/express-global-before-error-handling.spec.ts index ed1367ba..e951b040 100644 --- a/test/functional/express-global-before-error-handling.spec.ts +++ b/test/functional/express-global-before-error-handling.spec.ts @@ -5,37 +5,37 @@ import {Get} from "../../src/decorator/Get"; import {Middleware} from "../../src/decorator/Middleware"; import {ExpressErrorMiddlewareInterface} from "../../src/driver/express/ExpressErrorMiddlewareInterface"; import {ExpressMiddlewareInterface} from "../../src/driver/express/ExpressMiddlewareInterface"; - - -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosError} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import express from "express"; +import {axios} from "../utilities/axios"; describe("custom express global before middleware error handling", () => { + let errorHandlerCalled: boolean; + let errorHandlerName: string; + let expressServer: HttpServer; class CustomError extends Error { - name = "CustomError"; - message = "custom error message!"; + name = "CustomError"; + message = "custom error message!"; } - let errorHandlerCalled: boolean; - let errorHandlerName: string; - beforeEach(() => { errorHandlerCalled = undefined; errorHandlerName = undefined; }); - before(() => { - - - @Middleware({ type: "before" }) + beforeAll((done: DoneCallback) => { + @Middleware({type: "before"}) class GlobalBeforeMiddleware implements ExpressMiddlewareInterface { - use(request: any, response: any, next?: Function): any { - throw new CustomError(); + use(request: express.Request, response: express.Response, next: express.NextFunction): any { + throw new CustomError(); } } - @Middleware({ type: "after" }) + @Middleware({type: "after"}) class CustomErrorHandler implements ExpressErrorMiddlewareInterface { error(error: any, req: any, res: any, next: any) { errorHandlerCalled = true; @@ -47,28 +47,27 @@ describe("custom express global before middleware error handling", () => { @JsonController() class ExpressErrorHandlerController { - @Get("/answers") - answers() { - return { - id: 1, - title: "My answer" - }; - } + @Get("/answers") + answers() { + return { + id: 1, + title: "My answer" + }; + } } + + expressServer = createExpressServer().listen(3001, done); }); - let app: any; - before(done => app = createExpressServer({defaultErrorHandler: false}).listen(3001, done)); - after(done => app.close(done)); + afterAll((done: DoneCallback) => expressServer.close(done)); it("should call global error handler middleware with CustomError", () => { - return chakram - .get("http://127.0.0.1:3001/answers") - .then((response: any) => { - expect(errorHandlerCalled).to.be.true; - expect(errorHandlerName).to.equals("CustomError"); - expect(response).to.have.status(500); - }); + expect.assertions(3); + return axios.get("/answers") + .catch((error: AxiosError) => { + expect(errorHandlerCalled).toBeTruthy(); + expect(errorHandlerName).toEqual("CustomError"); + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); + }); }); - }); diff --git a/test/functional/express-middlewares.spec.ts b/test/functional/express-middlewares.spec.ts index 79dbcfb5..b41f4736 100644 --- a/test/functional/express-middlewares.spec.ts +++ b/test/functional/express-middlewares.spec.ts @@ -7,12 +7,15 @@ import {UseBefore} from "../../src/decorator/UseBefore"; import {Middleware} from "../../src/decorator/Middleware"; import {UseAfter} from "../../src/decorator/UseAfter"; import {NotAcceptableError} from "./../../src/http-error/NotAcceptableError"; - -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import express from "express"; +import {axios} from "../utilities/axios"; describe("express middlewares", () => { - + let expressServer: HttpServer; let useBefore: boolean, useAfter: boolean, useCustom: boolean, @@ -34,53 +37,42 @@ describe("express middlewares", () => { useCallOrder = undefined; }); - before(() => { - - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @Middleware({ type: "before" }) class TestGlobalBeforeMidleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { useGlobalBefore = true; useGlobalCallOrder = "setFromGlobalBefore"; next(); } - } @Middleware({ type: "after" }) class TestGlobalAfterMidleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { useGlobalAfter = true; useGlobalCallOrder = "setFromGlobalAfter"; next(); } - } class TestLoggerMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { useCustom = true; next(); } - } class TestCustomMiddlewareWhichThrows implements ExpressMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { throw new NotAcceptableError("TestCustomMiddlewareWhichThrows"); } - } @Controller() class ExpressMiddlewareController { - @Get("/blogs") blogs() { useGlobalCallOrder = "setFromController"; @@ -137,69 +129,68 @@ describe("express middlewares", () => { return "1234"; } } + + expressServer = createExpressServer().listen(3001, done); }); - let app: any; - before(done => app = createExpressServer().listen(3001, done)); - after(done => app.close(done)); + afterAll((done: DoneCallback) => expressServer.close(done)); it("should call a global middlewares", () => { - return chakram - .get("http://127.0.0.1:3001/blogs") - .then((response: any) => { - expect(useGlobalBefore).to.be.equal(true); - expect(useGlobalAfter).to.be.equal(true); - expect(useGlobalCallOrder).to.be.equal("setFromGlobalAfter"); - expect(response).to.have.status(200); + expect.assertions(4); + return axios.get("/blogs") + .then((response: AxiosResponse) => { + expect(useGlobalBefore).toEqual(true); + expect(useGlobalAfter).toEqual(true); + expect(useGlobalCallOrder).toEqual("setFromGlobalAfter"); + expect(response.status).toEqual(HttpStatusCodes.OK); }); }); it("should use a custom middleware when @UseBefore or @UseAfter is used", () => { - return chakram - .get("http://127.0.0.1:3001/questions") - .then((response: any) => { - expect(useCustom).to.be.equal(true); - expect(response).to.have.status(200); + expect.assertions(2); + return axios.get("/questions") + .then((response: AxiosResponse) => { + expect(useCustom).toEqual(true); + expect(response.status).toEqual(HttpStatusCodes.OK); }); }); it("should call middleware and call it before controller action when @UseBefore is used", () => { - return chakram - .get("http://127.0.0.1:3001/users") - .then((response: any) => { - expect(useBefore).to.be.equal(true); - expect(useCallOrder).to.be.equal("setFromController"); - expect(response).to.have.status(200); + expect.assertions(3); + return axios.get("/users") + .then((response: AxiosResponse) => { + expect(useBefore).toEqual(true); + expect(useCallOrder).toEqual("setFromController"); + expect(response.status).toEqual(HttpStatusCodes.OK); }); }); it("should call middleware and call it after controller action when @UseAfter is used", () => { - return chakram - .get("http://127.0.0.1:3001/photos") - .then((response: any) => { - expect(useAfter).to.be.equal(true); - expect(useCallOrder).to.be.equal("setFromUseAfter"); - expect(response).to.have.status(200); + expect.assertions(3); + return axios.get("/photos") + .then((response: AxiosResponse) => { + expect(useAfter).toEqual(true); + expect(useCallOrder).toEqual("setFromUseAfter"); + expect(response.status).toEqual(HttpStatusCodes.OK); }); }); it("should call before middleware and call after middleware when @UseAfter and @UseAfter are used", () => { - return chakram - .get("http://127.0.0.1:3001/posts") - .then((response: any) => { - expect(useBefore).to.be.equal(true); - expect(useAfter).to.be.equal(true); - expect(useCallOrder).to.be.equal("setFromUseAfter"); - expect(response).to.have.status(200); + expect.assertions(4); + return axios.get("/posts") + .then((response: AxiosResponse) => { + expect(useBefore).toEqual(true); + expect(useAfter).toEqual(true); + expect(useCallOrder).toEqual("setFromUseAfter"); + expect(response.status).toEqual(HttpStatusCodes.OK); }); }); it("should handle errors in custom middlewares", () => { - return chakram - .get("http://127.0.0.1:3001/customMiddlewareWichThrows") - .then((response: any) => { - expect(response).to.have.status(406); + expect.assertions(1); + return axios.get("/customMiddlewareWichThrows") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_ACCEPTABLE); }); }); - -}); \ No newline at end of file +}); diff --git a/test/functional/global-options.spec.ts b/test/functional/global-options.spec.ts index 381348c8..d7d950aa 100644 --- a/test/functional/global-options.spec.ts +++ b/test/functional/global-options.spec.ts @@ -2,110 +2,141 @@ import "reflect-metadata"; import {JsonController} from "../../src/decorator/JsonController"; import {Post} from "../../src/decorator/Post"; import {Body} from "../../src/decorator/Body"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; -const chakram = require("chakram"); -const expect = chakram.expect; - -export class User { +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; +import {Server as HttpServer} from "http"; +import DoneCallback = jest.DoneCallback; +import {AxiosResponse} from "axios"; +import HttpStatusCodes from "http-status-codes"; +import {axios} from "../utilities/axios"; + +class User { firstName: string; lastName: string; + getName(): string { return this.firstName + " " + this.lastName; } } -describe("routing-controllers global options", () => { +let initializedUser: User; +getMetadataArgsStorage().reset(); + +@JsonController() +class TestUserController { + @Post("/users") + postUsers(@Body() user: User) { + initializedUser = user; + return ""; + } - let initializedUser: User; + @Post(new RegExp("/(prefix|regex)/users")) + postUsersWithRegex(@Body() user: User) { + initializedUser = user; + return ""; + } +} +describe("routing-controllers global options", () => { + let expressServer: HttpServer; beforeEach(() => { initializedUser = undefined; }); - before(() => { - - // reset metadata args storage - getMetadataArgsStorage().reset(); - - @JsonController() - class TestUserController { - - @Post("/users") - postUsers(@Body() user: User) { - initializedUser = user; - return ""; - } - - @Post(new RegExp("/(prefix|regex)/users")) - postUsersWithRegex(@Body() user: User) { - initializedUser = user; - return ""; - } - - } - }); - - describe("useClassTransformer by default must be set to true", () => { - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); + describe("useClassTransformer default value", () => { + beforeEach((done: DoneCallback) => { + expressServer = createExpressServer({ + controllers: [TestUserController], + validation: false + }).listen(3001, done); + }); - assertRequest([3001, 3002], "post", "users", { firstName: "Umed", lastName: "Khudoiberdiev" }, response => { - expect(initializedUser).to.be.instanceOf(User); - expect(response).to.have.status(200); + afterEach((done: DoneCallback) => expressServer.close(done)); + + it("useClassTransformer by default must be set to true", () => { + expect.assertions(2); + return axios.post("/users", { + firstName: "Umed", + lastName: "Khudoiberdiev" + }).then((response: AxiosResponse) => { + expect(initializedUser).toBeInstanceOf(User); + expect(response.status).toEqual(HttpStatusCodes.OK); + }); }); }); describe("when useClassTransformer is set to true", () => { + beforeEach((done: DoneCallback) => { + expressServer = createExpressServer({ + classTransformer: true, + validation: false + }).listen(3001, done); + }); - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer({ classTransformer: true }).listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer({ classTransformer: true }).listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "post", "users", { firstName: "Umed", lastName: "Khudoiberdiev" }, response => { - expect(initializedUser).to.be.instanceOf(User); - expect(response).to.have.status(200); + afterEach((done: DoneCallback) => expressServer.close(done)); + + it("useClassTransformer is enabled", () => { + expect.assertions(2); + return axios.post("/users", { + firstName: "Umed", + lastName: "Khudoiberdiev" + }).then((response: AxiosResponse) => { + expect(initializedUser).toBeInstanceOf(User); + expect(response.status).toEqual(HttpStatusCodes.OK); + }); }); }); - describe("when useClassTransformer is not set", () => { - - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer({ classTransformer: false }).listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer({ classTransformer: false }).listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "post", "users", { firstName: "Umed", lastName: "Khudoiberdiev" }, response => { - expect(initializedUser).not.to.be.instanceOf(User); - expect(response).to.have.status(200); + describe("when useClassTransformer is set to false", () => { + beforeEach((done: DoneCallback) => { + expressServer = createExpressServer({ + classTransformer: false, + validation: false + }).listen(3001, done); + }); + + afterEach((done: DoneCallback) => expressServer.close(done)); + + it("useClassTransformer is disabled", () => { + expect.assertions(2); + return axios.post("/users", { + firstName: "Umed", + lastName: "Khudoiberdiev" + }).then((response: AxiosResponse) => { + expect(initializedUser).not.toBeInstanceOf(User); + expect(response.status).toEqual(HttpStatusCodes.OK); + }); }); }); describe("when routePrefix is used all controller routes should be appended by it", () => { - - let apps: any[] = []; - before(done => apps.push(createExpressServer({ routePrefix: "/api" }).listen(3001, done))); - before(done => apps.push(createExpressServer({ routePrefix: "api" }).listen(3002, done))); - before(done => apps.push(createKoaServer({ routePrefix: "/api" }).listen(3003, done))); - before(done => apps.push(createKoaServer({ routePrefix: "api" }).listen(3004, done))); - after(done => { apps.forEach(app => app.close()); done(); }); - - assertRequest([3001, 3002, 3003, 3004], "post", "api/users", { firstName: "Umed", lastName: "Khudoiberdiev" }, response => { - expect(initializedUser).to.be.instanceOf(User); - expect(response).to.have.status(200); + beforeEach((done: DoneCallback) => { + expressServer = createExpressServer({ + routePrefix: "api", + validation: false + }).listen(3001, done); }); - assertRequest([3001, 3002, 3003, 3004], "post", "api/regex/users", { firstName: "Umed", lastName: "Khudoiberdiev" }, response => { - expect(initializedUser).to.be.instanceOf(User); - expect(response).to.have.status(200); + afterEach((done: DoneCallback) => expressServer.close(done)); + + it("routePrefix is enabled", () => { + expect.assertions(4); + return Promise.all([ + axios.post("/api/users", { + firstName: "Umed", + lastName: "Khudoiberdiev" + }).then((response: AxiosResponse) => { + expect(initializedUser).toBeInstanceOf(User); + expect(response.status).toEqual(HttpStatusCodes.OK); + }), + axios.post("/api/regex/users", { + firstName: "Umed", + lastName: "Khudoiberdiev" + }).then((response: AxiosResponse) => { + expect(initializedUser).toBeInstanceOf(User); + expect(response.status).toEqual(HttpStatusCodes.OK); + }) + ]); }); }); -}); \ No newline at end of file +}); diff --git a/test/functional/interceptors.spec.ts b/test/functional/interceptors.spec.ts index 20fbce71..ae670a68 100644 --- a/test/functional/interceptors.spec.ts +++ b/test/functional/interceptors.spec.ts @@ -1,20 +1,21 @@ import "reflect-metadata"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {InterceptorInterface} from "../../src/InterceptorInterface"; import {Interceptor} from "../../src/decorator/Interceptor"; import {UseInterceptor} from "../../src/decorator/UseInterceptor"; import {Controller} from "../../src/decorator/Controller"; import {Get} from "../../src/decorator/Get"; import {Action} from "../../src/Action"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import {axios} from "../utilities/axios"; describe("interceptor", () => { + let expressServer: HttpServer; - before(() => { - - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @Interceptor() @@ -49,7 +50,6 @@ describe("interceptor", () => { @Controller() @UseInterceptor(ByeWordInterceptor) class HandledController { - @Get("/users") @UseInterceptor((action: Action, result: any) => { return result.replace(/hello/gi, "hello world"); @@ -79,55 +79,60 @@ describe("interceptor", () => { photos(): any { return "hello world"; } - } + expressServer = createExpressServer().listen(3001, done); }); - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - describe("custom interceptor function should replace returned content", () => { - assertRequest([3001, 3002], "get", "users", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("damn hello world"); - }); - }); + afterAll((done: DoneCallback) => expressServer.close(done)); - describe("custom interceptor class should replace returned content", () => { - assertRequest([3001, 3002], "get", "posts", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("this post contains *** bad words"); - }); + it("custom interceptor function should replace returned content", () => { + expect.assertions(3); + return axios.get("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("damn hello world"); + }); }); - describe("custom interceptor class used on the whole controller should replace returned content", () => { - assertRequest([3001, 3002], "get", "questions", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("hello world"); - }); + it("custom interceptor class should replace returned content", () => { + expect.assertions(3); + return axios.get("/posts") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("this post contains *** bad words"); + }); }); - describe("global interceptor class should replace returned content", () => { - assertRequest([3001, 3002], "get", "files", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("hello world"); - }); + it("custom interceptor class used on the whole controller should replace returned content", () => { + expect.assertions(3); + return axios.get("/questions") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("hello world"); + }); }); - describe("interceptors should support promises", () => { - assertRequest([3001, 3002], "get", "photos", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.equal("bye world"); - }); + it("global interceptor class should replace returned content", () => { + expect.assertions(3); + return axios.get("/files") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("hello world"); + }); }); -}); \ No newline at end of file + it("interceptors should support promises", () => { + expect.assertions(3); + return axios.get("/photos") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("bye world"); + }); + }); +}); diff --git a/test/functional/json-controller-methods.spec.ts b/test/functional/json-controller-methods.spec.ts index b75c5adc..45d63d1a 100644 --- a/test/functional/json-controller-methods.spec.ts +++ b/test/functional/json-controller-methods.spec.ts @@ -7,16 +7,17 @@ import {Head} from "../../src/decorator/Head"; import {Delete} from "../../src/decorator/Delete"; import {Patch} from "../../src/decorator/Patch"; import {Put} from "../../src/decorator/Put"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import {axios} from "../utilities/axios"; describe("json-controller methods", () => { + let expressServer: HttpServer; - before(() => { - - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @JsonController() @@ -31,48 +32,56 @@ describe("json-controller methods", () => { name: "Bakha" }]; } + @Post("/users") post() { return { status: "saved" }; } + @Put("/users") put() { return { status: "updated" }; } + @Patch("/users") patch() { return { status: "patched" }; } + @Delete("/users") delete() { return { status: "removed" }; } + @Head("/users") head() { return { thisWillNot: "beSent" }; } + @Method("post", "/categories") postCategories() { return { status: "posted" }; } + @Method("delete", "/categories") getCategories() { return { status: "removed" }; } + @Get("/users/:id") getUserById() { return { @@ -80,6 +89,7 @@ describe("json-controller methods", () => { name: "Umed" }; } + @Get(/\/categories\/[\d+]/) getCategoryById() { return { @@ -87,6 +97,7 @@ describe("json-controller methods", () => { name: "People" }; } + @Get("/posts/:id(\\d+)") getPostById() { return { @@ -94,6 +105,7 @@ describe("json-controller methods", () => { title: "About People" }; } + @Get("/posts-from-db") getPostFromDb() { return new Promise((ok, fail) => { @@ -105,6 +117,7 @@ describe("json-controller methods", () => { }, 500); }); } + @Get("/posts-from-failed-db") getPostFromFailedDb() { return new Promise((ok, fail) => { @@ -117,162 +130,189 @@ describe("json-controller methods", () => { }); } } - }); - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - describe("get should respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "get", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.instanceOf(Array); - expect(response.body).to.be.eql([{ - id: 1, - name: "Umed" - }, { - id: 2, - name: "Bakha" - }]); - }); + expressServer = createExpressServer().listen(3001, done); }); - - describe("post respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "post", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - status: "saved" + + afterAll((done: DoneCallback) => expressServer.close(done)); + + it("get should respond with proper status code, headers and body content", () => { + expect.assertions(4); + return axios.get("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toBeInstanceOf(Array); + expect(response.data).toEqual([{ + id: 1, + name: "Umed" + }, { + id: 2, + name: "Bakha" + }]); }); - }); }); - - describe("put respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "put", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - status: "updated" + + it("post respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.post("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + status: "saved" + }); }); - }); }); - - describe("patch respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "patch", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - status: "patched" + + it("put respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.put("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + status: "updated" + }); }); - }); }); - - describe("delete respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "delete", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - status: "removed" + + it("patch respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.patch("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + status: "patched" + }); }); - }); }); - describe("head respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "head", "users", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.undefined; - }); + it("delete respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.delete("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + status: "removed" + }); + }); }); - describe("custom method (post) respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "post", "categories", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - status: "posted" + it("head respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.head("/users") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual(""); }); - }); }); - describe("custom method (delete) respond with proper status code, headers and body content", () => { - assertRequest([3001, 3002], "delete", "categories", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - status: "removed" + it("custom method (post) respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.post("/categories") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + status: "posted" + }); }); - }); }); - describe("route should work with parameter", () => { - assertRequest([3001, 3002], "get", "users/umed", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - id: 1, - name: "Umed" + it("custom method (delete) respond with proper status code, headers and body content", () => { + expect.assertions(3); + return axios.delete("/categories") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + status: "removed" + }); }); - }); }); - describe("route should work with regexp parameter", () => { - assertRequest([3001, 3002], "get", "categories/1", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - id: 1, - name: "People" + it("route should work with parameter", () => { + expect.assertions(3); + return axios.get("/users/umed") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + id: 1, + name: "Umed" + }); }); - }); }); - describe("should respond with 404 when regexp does not match", () => { - assertRequest([3001, 3002], "get", "categories/umed", response => { - expect(response).to.have.status(404); - }); + it("route should work with regexp parameter", () => { + expect.assertions(3); + return axios.get("/categories/1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + id: 1, + name: "People" + }); + }); }); - describe("route should work with string regexp parameter", () => { - assertRequest([3001, 3002], "get", "posts/1", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - id: 1, - title: "About People" + it("should respond with 404 when regexp does not match", () => { + expect.assertions(1); + return axios.get("/categories/umed") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); }); - }); }); - describe("should respond with 404 when regexp does not match", () => { - assertRequest([3001, 3002], "get", "posts/U", response => { - expect(response).to.have.status(404); - }); + it("route should work with string regexp parameter", () => { + expect.assertions(3); + return axios.get("/posts/1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + id: 1, + title: "About People" + }); + }); }); - describe("should return result from a promise", () => { - assertRequest([3001, 3002], "get", "posts-from-db", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - id: 1, - title: "Hello database post" + it("should respond with 404 when regexp does not match", () => { + expect.assertions(1); + return axios.get("/posts/U") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); }); - }); }); - describe("should respond with 500 if promise failed", () => { - assertRequest([3001, 3002], "get", "posts-from-failed-db", response => { - expect(response).to.have.status(500); - expect(response).to.have.header("content-type", "application/json; charset=utf-8"); - expect(response.body).to.be.eql({ - code: 10954, - message: "Cannot connect to db" + it("should return result from a promise", () => { + expect.assertions(3); + return axios.get("/posts-from-db") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(response.data).toEqual({ + id: 1, + title: "Hello database post" + }); }); - }); }); -}); \ No newline at end of file + it("should respond with 500 if promise failed", () => { + expect.assertions(3); + return axios.get("/posts-from-failed-db") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); + expect(error.response.headers["content-type"]).toEqual("application/json; charset=utf-8"); + expect(error.response.data).toEqual({ + code: 10954, + message: "Cannot connect to db" + }); + }); + }); +}); diff --git a/test/functional/koa-middlewares.spec.ts b/test/functional/koa-middlewares.spec.ts deleted file mode 100644 index 705f1b3f..00000000 --- a/test/functional/koa-middlewares.spec.ts +++ /dev/null @@ -1,202 +0,0 @@ -import "reflect-metadata"; -import {Controller} from "../../src/decorator/Controller"; -import {Get} from "../../src/decorator/Get"; -import {UseBefore} from "../../src/decorator/UseBefore"; -import {Middleware} from "../../src/decorator/Middleware"; -import {UseAfter} from "../../src/decorator/UseAfter"; -import {createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {KoaMiddlewareInterface} from "../../src/driver/koa/KoaMiddlewareInterface"; -import {NotAcceptableError} from "./../../src/http-error/NotAcceptableError"; - -const chakram = require("chakram"); -const expect = chakram.expect; - -describe("koa middlewares", () => { - - let useBefore: boolean, - useAfter: boolean, - useCustom: boolean, - useGlobalBefore: boolean, - useGlobalAfter: boolean, - useCallOrder: string, - useGlobalCallOrder: string; - - beforeEach(() => { - useBefore = false; - useAfter = undefined; - useCustom = undefined; - useGlobalBefore = undefined; - useGlobalAfter = undefined; - useCallOrder = undefined; - }); - - before(() => { - - // reset metadata args storage - getMetadataArgsStorage().reset(); - - @Middleware({ type: "before" }) - class TestGlobalBeforeKoaMidleware implements KoaMiddlewareInterface { - - use(context: any, next?: Function): any { - useGlobalBefore = true; - useGlobalCallOrder = "setFromGlobalBefore"; - return next(); - } - - } - - @Middleware({ type: "after" }) - class TestGlobalAfterKoaMidleware implements KoaMiddlewareInterface { - - use(context: any, next?: Function): any { - useGlobalAfter = true; - useGlobalCallOrder = "setFromGlobalAfter"; - return next(); - } - - } - - class TestLoggerKoaMiddleware implements KoaMiddlewareInterface { - - use(context: any, next?: Function): any { - useCustom = true; - return next(); - } - - } - - class TestCustomMiddlewareWhichThrows implements KoaMiddlewareInterface { - - use(request: any, response: any, next?: Function): any { - throw new NotAcceptableError("TestCustomMiddlewareWhichThrows"); - } - - } - - @Controller() - class KoaMiddlewareController { - - @Get("/blogs") - blogs() { - useGlobalCallOrder = "setFromController"; - return "1234"; - } - - @Get("/questions") - @UseBefore(TestLoggerKoaMiddleware) - questions() { - return "1234"; - } - - @Get("/users") - @UseBefore(function (context: any, next: Function) { - useBefore = true; - useCallOrder = "setFromUseBefore"; - return next(); - }) - users() { - useCallOrder = "setFromController"; - return "1234"; - } - - @Get("/photos") - @UseAfter(function (context: any, next: Function) { - useAfter = true; - useCallOrder = "setFromUseAfter"; - return next(); - }) - photos() { - useCallOrder = "setFromController"; - return "1234"; - } - - @Get("/posts") - @UseBefore(function (context: any, next: Function) { - useBefore = true; - useCallOrder = "setFromUseBefore"; - return next(); - }) - @UseAfter(function (context: any, next: Function) { - useAfter = true; - useCallOrder = "setFromUseAfter"; - return next(); - }) - posts() { - useCallOrder = "setFromController"; - return "1234"; - } - - @Get("/customMiddlewareWichThrows") - @UseBefore(TestCustomMiddlewareWhichThrows) - customMiddlewareWichThrows() { - return "1234"; - } - - } - }); - - let app: any; - before(done => app = createKoaServer().listen(3001, done)); - after(done => app.close(done)); - - it("should call a global middlewares", () => { - return chakram - .get("http://127.0.0.1:3001/blogs") - .then((response: any) => { - expect(useGlobalBefore).to.be.equal(true); - expect(useGlobalAfter).to.be.equal(true); - expect(useGlobalCallOrder).to.be.equal("setFromGlobalAfter"); - expect(response).to.have.status(200); - }); - }); - - it("should use a custom middleware when @UseBefore or @UseAfter is used", () => { - return chakram - .get("http://127.0.0.1:3001/questions") - .then((response: any) => { - expect(useCustom).to.be.equal(true); - expect(response).to.have.status(200); - }); - }); - - it("should call middleware and call it before controller action when @UseBefore is used", () => { - return chakram - .get("http://127.0.0.1:3001/users") - .then((response: any) => { - expect(useBefore).to.be.equal(true); - expect(useCallOrder).to.be.equal("setFromController"); - expect(response).to.have.status(200); - }); - }); - - it("should call middleware and call it after controller action when @UseAfter is used", () => { - return chakram - .get("http://127.0.0.1:3001/photos") - .then((response: any) => { - expect(useAfter).to.be.equal(true); - expect(useCallOrder).to.be.equal("setFromUseAfter"); - expect(response).to.have.status(200); - }); - }); - - it("should call before middleware and call after middleware when @UseAfter and @UseAfter are used", () => { - return chakram - .get("http://127.0.0.1:3001/posts") - .then((response: any) => { - expect(useBefore).to.be.equal(true); - expect(useAfter).to.be.equal(true); - expect(useCallOrder).to.be.equal("setFromUseAfter"); - expect(response).to.have.status(200); - }); - }); - - it("should handle errors in custom middlewares", () => { - return chakram - .get("http://127.0.0.1:3001/customMiddlewareWichThrows") - .then((response: any) => { - expect(response).to.have.status(406); - }); - }); - -}); \ No newline at end of file diff --git a/test/functional/load-from-directory.spec.ts b/test/functional/load-from-directory.spec.ts index 74e3557a..b88b1572 100644 --- a/test/functional/load-from-directory.spec.ts +++ b/test/functional/load-from-directory.spec.ts @@ -1,158 +1,114 @@ import "reflect-metadata"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {defaultFakeService} from "../fakes/global-options/FakeService"; import {Controller} from "../../src/decorator/Controller"; import {Get} from "../../src/decorator/Get"; -const chakram = require("chakram"); -const expect = chakram.expect; - -describe("controllers and middlewares bulk loading from directories", () => { - - describe("loading all controllers from the given directories", () => { - - before(() => getMetadataArgsStorage().reset()); - - const serverOptions = { +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import {axios} from "../utilities/axios"; + +describe("loading all controllers from the given directories", () => { + let expressServer: HttpServer; + + beforeAll((done: DoneCallback) => { + getMetadataArgsStorage().reset(); + expressServer = createExpressServer({ controllers: [ __dirname + "/../fakes/global-options/first-controllers/**/*{.js,.ts}", __dirname + "/../fakes/global-options/second-controllers/*{.js,.ts}" ] - }; - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer(serverOptions).listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer(serverOptions).listen(3002, done)); - after(done => koaApp.close(done)); - - assertRequest([3001, 3002], "get", "posts", response => { - expect(response.body).to.be.eql([{ id: 1, title: "#1" }, { id: 2, title: "#2" }]); - }); - - assertRequest([3001, 3002], "get", "questions", response => { - expect(response.body).to.be.eql([{ id: 1, title: "#1" }, { id: 2, title: "#2" }]); - }); - - assertRequest([3001, 3002], "get", "answers", response => { - expect(response.body).to.be.eql([{ id: 1, title: "#1" }, { id: 2, title: "#2" }]); - }); - - assertRequest([3001, 3002], "get", "photos", response => { - expect(response.body).to.be.eql("Hello photos"); - }); - - assertRequest([3001, 3002], "get", "videos", response => { - expect(response.body).to.be.eql("Hello videos"); - }); + }).listen(3001, done); }); - describe("loading all express middlewares and error handlers from the given directories", () => { - - before(() => getMetadataArgsStorage().reset()); + afterAll((done: DoneCallback) => expressServer.close(done)); + + it("should load all controllers", () => { + expect.assertions(10); + return Promise.all([ + axios.get("/posts") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{id: 1, title: "#1"}, {id: 2, title: "#2"}]); + }), + axios.get("/questions") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{id: 1, title: "#1"}, {id: 2, title: "#2"}]); + }), + axios.get("/answers") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual([{id: 1, title: "#1"}, {id: 2, title: "#2"}]); + }), + axios.get("/photos") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual("Hello photos"); + }), + axios.get("/videos") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual("Hello videos"); + }) + ]); + }); +}); - before(() => { - @Controller() - class ExpressMiddlewareDirectoriesController { +describe("loading all express middlewares and error handlers from the given directories", () => { + let expressServer: HttpServer; - @Get("/publications") - publications(): any[] { - return []; - } + beforeAll((done: DoneCallback) => { + getMetadataArgsStorage().reset(); - @Get("/articles") - articles(): any[] { - throw new Error("Cannot load articles"); - } + @Controller() + class ExpressMiddlewareDirectoriesController { + @Get("/publications") + publications(): any[] { + return []; + } + @Get("/articles") + articles(): any[] { + throw new Error("Cannot load articles"); } - }); + } - const serverOptions = { + expressServer = createExpressServer({ middlewares: [ __dirname + "/../fakes/global-options/express-middlewares/**/*{.js,.ts}" ], - }; - let expressApp: any; - before(done => expressApp = createExpressServer(serverOptions).listen(3001, done)); - after(done => expressApp.close(done)); - - beforeEach(() => defaultFakeService.reset()); - - assertRequest([3001], "get", "publications", response => { - expect(response).to.have.status(200); - expect(defaultFakeService.postMiddlewareCalled).to.be.true; - expect(defaultFakeService.questionMiddlewareCalled).to.be.true; - expect(defaultFakeService.questionErrorMiddlewareCalled).to.be.false; - expect(defaultFakeService.fileMiddlewareCalled).to.be.false; - expect(defaultFakeService.videoMiddlewareCalled).to.be.false; - }); - - assertRequest([3001], "get", "articles", response => { - expect(response).to.have.status(500); - expect(defaultFakeService.postMiddlewareCalled).to.be.true; - expect(defaultFakeService.questionMiddlewareCalled).to.be.true; - expect(defaultFakeService.questionErrorMiddlewareCalled).to.be.true; - expect(defaultFakeService.fileMiddlewareCalled).to.be.false; - expect(defaultFakeService.videoMiddlewareCalled).to.be.false; - }); - + }).listen(3001, done); }); - describe("loading all koa middlewares from the given directories", () => { - - before(() => getMetadataArgsStorage().reset()); - - before(() => { - @Controller() - class KoaMiddlewareDirectoriesController { - - @Get("/publications") - publications(): any[] { - return []; - } - - @Get("/articles") - articles(): any[] { - throw new Error("Cannot load articles"); - } - - } - }); - - const serverOptions = { - middlewares: [ - __dirname + "/../fakes/global-options/koa-middlewares/**/*{.js,.ts}" - ] - }; - let koaApp: any; - before(done => koaApp = createKoaServer(serverOptions).listen(3002, done)); - after(done => koaApp.close(done)); - - beforeEach(() => defaultFakeService.reset()); - - assertRequest([3002], "get", "publications", response => { - expect(response).to.have.status(200); - expect(defaultFakeService.postMiddlewareCalled).to.be.false; - expect(defaultFakeService.questionMiddlewareCalled).to.be.false; - expect(defaultFakeService.questionErrorMiddlewareCalled).to.be.false; - expect(defaultFakeService.fileMiddlewareCalled).to.be.true; - expect(defaultFakeService.videoMiddlewareCalled).to.be.true; - }); - - assertRequest([3002], "get", "articles", response => { - // expect(response).to.have.status(500); - expect(defaultFakeService.postMiddlewareCalled).to.be.false; - expect(defaultFakeService.questionMiddlewareCalled).to.be.false; - expect(defaultFakeService.questionErrorMiddlewareCalled).to.be.false; - expect(defaultFakeService.fileMiddlewareCalled).to.be.true; - expect(defaultFakeService.videoMiddlewareCalled).to.be.true; - }); - + afterAll((done: DoneCallback) => expressServer.close(done)); + + beforeEach(() => defaultFakeService.reset()); + + it("should succeed", () => { + expect.assertions(6); + return axios.get("/publications") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(defaultFakeService.postMiddlewareCalled).toBeTruthy(); + expect(defaultFakeService.questionMiddlewareCalled).toBeTruthy(); + expect(defaultFakeService.questionErrorMiddlewareCalled).toBeFalsy(); + expect(defaultFakeService.fileMiddlewareCalled).toBeFalsy(); + expect(defaultFakeService.videoMiddlewareCalled).toBeFalsy(); + }); }); + it("should fail", () => { + expect.assertions(6) + return axios.get("/articles") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.INTERNAL_SERVER_ERROR); + expect(defaultFakeService.postMiddlewareCalled).toBeTruthy(); + expect(defaultFakeService.questionMiddlewareCalled).toBeTruthy(); + expect(defaultFakeService.questionErrorMiddlewareCalled).toBeTruthy(); + expect(defaultFakeService.fileMiddlewareCalled).toBeFalsy(); + expect(defaultFakeService.videoMiddlewareCalled).toBeFalsy(); + }); + }) }); - - -/* -fakeContainer.services[(FakeService as any).name] = sinon.stub(new FakeService()); -// container: fakeContainer*/ diff --git a/test/functional/middlewares-order.spec.ts b/test/functional/middlewares-order.spec.ts index 8e01e8ef..f19c9704 100644 --- a/test/functional/middlewares-order.spec.ts +++ b/test/functional/middlewares-order.spec.ts @@ -3,161 +3,134 @@ import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {ExpressMiddlewareInterface} from "../../src/driver/express/ExpressMiddlewareInterface"; import {Controller} from "../../src/decorator/Controller"; import {Get} from "../../src/decorator/Get"; -import {UseBefore} from "../../src/decorator/UseBefore"; import {Middleware} from "../../src/decorator/Middleware"; -import {UseAfter} from "../../src/decorator/UseAfter"; -import {NotAcceptableError} from "./../../src/http-error/NotAcceptableError"; -import {ExpressErrorMiddlewareInterface} from "./../../src/driver/express/ExpressErrorMiddlewareInterface"; -const chakram = require("chakram"); -const expect = chakram.expect; - -describe("order of middlewares", () => { - - describe("loaded direct from array", () => { - - let middlewaresOrder: number[]; - - beforeEach(() => { - middlewaresOrder = []; - }); - - let app: any; - before(done => { - - // reset metadata args storage - getMetadataArgsStorage().reset(); - - @Middleware({ type: "after" }) - class ThirdAfterMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next: (err?: any) => any) { - middlewaresOrder.push(3); - next(); - } - +import {AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import express from "express"; +import {axios} from "../utilities/axios"; + +describe("loaded direct from array", () => { + let expressServer: HttpServer; + let middlewaresOrder: number[]; + + beforeEach(() => { + middlewaresOrder = []; + }); + + beforeAll((done: DoneCallback) => { + getMetadataArgsStorage().reset(); + + @Middleware({type: "after"}) + class ThirdAfterMiddleware implements ExpressMiddlewareInterface { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { + middlewaresOrder.push(3); + next(); } - - @Middleware({ type: "after" }) - class FirstAfterMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next: (err?: any) => any) { - middlewaresOrder.push(1); - next(); - } - + } + + @Middleware({type: "after"}) + class FirstAfterMiddleware implements ExpressMiddlewareInterface { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { + middlewaresOrder.push(1); + next(); } - - @Middleware({ type: "after" }) - class SecondAfterMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next: (err?: any) => any) { - middlewaresOrder.push(2); - next(); - } - + } + + @Middleware({type: "after"}) + class SecondAfterMiddleware implements ExpressMiddlewareInterface { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { + middlewaresOrder.push(2); + next(); } - - @Controller() - class ExpressMiddlewareController { - - @Get("/test") - test() { - return "OK"; - } - + } + + @Controller() + class ExpressMiddlewareController { + @Get("/test") + test() { + return "OK"; } - - app = createExpressServer({ - middlewares: [FirstAfterMiddleware, SecondAfterMiddleware, ThirdAfterMiddleware] - }).listen(3001, done); - }); - - after(done => app.close(done)); - - it("should call middlewares in order defined by items order", () => { - return chakram - .get("http://127.0.0.1:3001/test") - .then((response: any) => { - expect(response).to.have.status(200); - expect(middlewaresOrder[0]).to.equal(1); - expect(middlewaresOrder[1]).to.equal(2); - expect(middlewaresOrder[2]).to.equal(3); - }); - }); - + } + + expressServer = createExpressServer({ + middlewares: [FirstAfterMiddleware, SecondAfterMiddleware, ThirdAfterMiddleware] + }).listen(3001, done); + }); + + afterAll((done: DoneCallback) => expressServer.close(done)); + + it("should call middlewares in order defined by items order", () => { + expect.assertions(4); + return axios.get("/test") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(middlewaresOrder[0]).toEqual(1); + expect(middlewaresOrder[1]).toEqual(2); + expect(middlewaresOrder[2]).toEqual(3); + }); }); - - describe("specified by priority option", () => { - - let middlewaresOrder: number[]; - - beforeEach(() => { - middlewaresOrder = []; - }); - - let app: any; - before(done => { - - // reset metadata args storage - getMetadataArgsStorage().reset(); - - @Middleware({ type: "after", priority: 0 }) - class ThirdAfterMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next: (err?: any) => any) { - middlewaresOrder.push(3); - next(); - } - +}); + +describe("specified by priority option", () => { + let expressServer: HttpServer; + let middlewaresOrder: number[]; + + beforeEach(() => { + middlewaresOrder = []; + }); + + beforeAll((done: DoneCallback) => { + getMetadataArgsStorage().reset(); + + @Middleware({type: "after", priority: 0}) + class ThirdAfterMiddleware implements ExpressMiddlewareInterface { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { + middlewaresOrder.push(3); + next(); } - - @Middleware({ type: "after", priority: 8 }) - class FirstAfterMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next: (err?: any) => any) { - middlewaresOrder.push(1); - next(); - } - + } + + @Middleware({type: "after", priority: 8}) + class FirstAfterMiddleware implements ExpressMiddlewareInterface { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { + middlewaresOrder.push(1); + next(); } - - @Middleware({ type: "after", priority: 4 }) - class SecondAfterMiddleware implements ExpressMiddlewareInterface { - - use(request: any, response: any, next: (err?: any) => any) { - middlewaresOrder.push(2); - next(); - } - + } + + @Middleware({type: "after", priority: 4}) + class SecondAfterMiddleware implements ExpressMiddlewareInterface { + use(request: express.Request, response: express.Response, next: express.NextFunction): any { + middlewaresOrder.push(2); + next(); } - - @Controller() - class ExpressMiddlewareController { - - @Get("/test") - test() { - return "OK"; - } - + } + + @Controller() + class ExpressMiddlewareController { + @Get("/test") + test() { + return "OK"; } - - app = createExpressServer({ - middlewares: [SecondAfterMiddleware, ThirdAfterMiddleware, FirstAfterMiddleware] - }).listen(3001, done); - }); - - after(done => app.close(done)); - - it("should call middlewares in order defined by priority parameter of decorator", () => { - return chakram - .get("http://127.0.0.1:3001/test") - .then((response: any) => { - expect(response).to.have.status(200); - expect(middlewaresOrder[0]).to.equal(1); - expect(middlewaresOrder[1]).to.equal(2); - expect(middlewaresOrder[2]).to.equal(3); - }); - }); + } + + expressServer = createExpressServer({ + middlewares: [SecondAfterMiddleware, ThirdAfterMiddleware, FirstAfterMiddleware] + }).listen(3001, done); }); + afterAll((done: DoneCallback) => expressServer.close(done)); + + it("should call middlewares in order defined by priority parameter of decorator", () => { + expect.assertions(4); + return axios.get("/test") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(middlewaresOrder[0]).toEqual(1); + expect(middlewaresOrder[1]).toEqual(2); + expect(middlewaresOrder[2]).toEqual(3); + }); + }); }); diff --git a/test/functional/other-controller-decorators.spec.ts b/test/functional/other-controller-decorators.spec.ts index 2923700d..008eb318 100644 --- a/test/functional/other-controller-decorators.spec.ts +++ b/test/functional/other-controller-decorators.spec.ts @@ -3,8 +3,7 @@ import {Controller} from "../../src/decorator/Controller"; import {Get} from "../../src/decorator/Get"; import {Param} from "../../src/decorator/Param"; import {Post} from "../../src/decorator/Post"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage, OnNull} from "../../src/index"; -import {assertRequest} from "./test-utils"; +import {createExpressServer, getMetadataArgsStorage, OnNull} from "../../src/index"; import {HttpCode} from "../../src/decorator/HttpCode"; import {ContentType} from "../../src/decorator/ContentType"; import {Header} from "../../src/decorator/Header"; @@ -14,33 +13,33 @@ import {OnUndefined} from "../../src/decorator/OnUndefined"; import {HttpError} from "../../src/http-error/HttpError"; import {Action} from "../../src/Action"; import {JsonController} from "../../src/decorator/JsonController"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosError, AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import {axios} from "../utilities/axios"; describe("other controller decorators", () => { - before(() => { + let expressServer: HttpServer; - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); class QuestionNotFoundError extends HttpError { - constructor(action: Action) { super(404, `Question was not found!`); Object.setPrototypeOf(this, QuestionNotFoundError.prototype); } - } @Controller() class OtherDectoratorsController { - @Post("/users") @HttpCode(201) getUsers() { return "User has been created"; } - + @Get("/admin") @HttpCode(403) getAdmin() { @@ -72,7 +71,7 @@ describe("other controller decorators", () => { if (id === 4) { return undefined; } - + return new Promise((ok, fail) => { if (id === 1) { ok("Photo"); @@ -119,131 +118,161 @@ describe("other controller decorators", () => { goToGithub() { // todo: need test for this one return "Hello, github"; } - } @JsonController() class JsonOtherDectoratorsController { - @Get("/questions/:id") @OnUndefined(QuestionNotFoundError) getPosts(@Param("id") id: number) { return new Promise((ok, fail) => { if (id === 1) { ok("Question"); - } else { ok(undefined); } }); } - } - }); - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); - - describe("should return httpCode set by @HttpCode decorator", () => { - assertRequest([3001, 3002], "post", "users", { name: "Umed" }, response => { - expect(response).to.have.status(201); - expect(response.body).to.be.eql("User has been created"); - }); - - assertRequest([3001, 3002], "get", "admin", response => { - expect(response).to.have.status(403); - expect(response.body).to.be.eql("Access is denied"); - }); + expressServer = createExpressServer().listen(3001, done); }); - describe("should return custom code when @OnNull", () => { - assertRequest([3001, 3002], "get", "posts/1", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql("Post"); - }); - assertRequest([3001, 3002], "get", "posts/2", response => { - expect(response).to.have.status(200); - }); - assertRequest([3001, 3002], "get", "posts/3", response => { - expect(response).to.have.status(404); - }); - assertRequest([3001, 3002], "get", "posts/4", response => { - expect(response).to.have.status(404); // this is expected because for undefined 404 is given by default - }); - assertRequest([3001, 3002], "get", "posts/5", response => { - expect(response).to.have.status(404); // this is expected because for undefined 404 is given by default - }); + afterAll((done: DoneCallback) => expressServer.close(done)); + + it("should return httpCode set by @HttpCode decorator", () => { + expect.assertions(4); + return Promise.all([ + axios.post("/users", { + name: "Umed" + }).then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.CREATED); + expect(response.data).toEqual("User has been created"); + }), + axios.get("/admin") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.FORBIDDEN); + expect(error.response.data).toEqual("Access is denied"); + }) + ]); }); - - describe("should return custom error message and code when @OnUndefined is used with Error class", () => { - assertRequest([3001, 3002], "get", "questions/1", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.equal("Question"); - }); - assertRequest([3001, 3002], "get", "questions/2", response => { - expect(response).to.have.status(404); - expect(response.body.name).to.be.equal("QuestionNotFoundError"); - expect(response.body.message).to.be.equal("Question was not found!"); - }); - assertRequest([3001, 3002], "get", "questions/3", response => { - expect(response).to.have.status(404); // because of null - expect(response.body.name).to.be.equal("QuestionNotFoundError"); - expect(response.body.message).to.be.equal("Question was not found!"); - }); + + it("should return custom code when @OnNull", () => { + expect.assertions(6); + return Promise.all([ + axios.get("/posts/1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual("Post"); + }), + axios.get("/posts/2") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + }), + axios.get("/posts/3") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + }), + axios.get("/posts/4") + .catch((error: AxiosError) => { + // this is expected because for undefined 404 is given by default + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + }), + axios.get("/posts/5") + .catch((error: AxiosError) => { + // this is expected because for undefined 404 is given by default + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + }) + ]); }); - describe("should return custom code when @OnUndefined", () => { - assertRequest([3001, 3002], "get", "photos/1", response => { - expect(response).to.have.status(200); - expect(response.body).to.be.eql("Photo"); - }); - assertRequest([3001, 3002], "get", "photos/2", response => { - expect(response).to.have.status(200); - }); - assertRequest([3001, 3002], "get", "photos/3", response => { - expect(response).to.have.status(204); // because of null - }); - assertRequest([3001, 3002], "get", "photos/4", response => { - expect(response).to.have.status(201); - }); - assertRequest([3001, 3002], "get", "photos/5", response => { - expect(response).to.have.status(201); - }); + it("should return custom error message and code when @OnUndefined is used with Error class", () => { + expect.assertions(8); + return Promise.all([ + axios.get("/questions/1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual("Question"); + }), + axios.get("/questions/2") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + expect(error.response.data.name).toEqual("QuestionNotFoundError"); + expect(error.response.data.message).toEqual("Question was not found!"); + }), + axios.get("/questions/3") + .catch((error: AxiosError) => { + expect(error.response.status).toEqual(HttpStatusCodes.NOT_FOUND); + expect(error.response.data.name).toEqual("QuestionNotFoundError"); + expect(error.response.data.message).toEqual("Question was not found!"); + }) + ]); }); - describe("should return content-type in the response when @ContentType is used", () => { - assertRequest([3001, 3002], "get", "homepage", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/html; charset=utf-8"); - expect(response.body).to.be.eql("Hello world"); - }); + it("should return custom code when @OnUndefined", () => { + expect.assertions(6); + return Promise.all([ + axios.get("/photos/1") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toEqual("Photo"); + }), + axios.get("/photos/2") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + }), + axios.get("/photos/3") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.NO_CONTENT); + }), + axios.get("/photos/4") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.CREATED); + }), + axios.get("/photos/5") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.CREATED); + }) + ]); }); - describe("should return content-type in the response when @ContentType is used", () => { - assertRequest([3001, 3002], "get", "textpage", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("content-type", "text/plain; charset=utf-8"); - expect(response.body).to.be.eql("Hello text"); - }); + it("should return content-type in the response when @ContentType is used", () => { + expect.assertions(3); + return axios.get("/homepage") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/html; charset=utf-8"); + expect(response.data).toEqual("Hello world"); + }); }); - describe("should return response with custom headers when @Header is used", () => { - assertRequest([3001, 3002], "get", "userdash", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("authorization", "Barer abcdefg"); - expect(response).to.have.header("development-mode", "enabled"); - expect(response.body).to.be.eql("Hello, User"); - }); + it("should return content-type in the response when @ContentType is used", () => { + expect.assertions(3); + return axios.get("/textpage") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/plain; charset=utf-8"); + expect(response.data).toEqual("Hello text"); + }); }); - describe("should relocate to new location when @Location is used", () => { - assertRequest([3001, 3002], "get", "github", response => { - expect(response).to.have.status(200); - expect(response).to.have.header("location", "http://github.com"); - }); + it("should return response with custom headers when @Header is used", () => { + expect.assertions(4); + return axios.get("/userdash") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["authorization"]).toEqual("Barer abcdefg"); + expect(response.headers["development-mode"]).toEqual("enabled"); + expect(response.data).toEqual("Hello, User"); + }); }); -}); \ No newline at end of file + it("should relocate to new location when @Location is used", () => { + expect.assertions(2); + return axios.get("/github") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["location"]).toEqual("http://github.com"); + }); + }); +}); diff --git a/test/functional/redirect-decorator.spec.ts b/test/functional/redirect-decorator.spec.ts index 3209a039..7671bec2 100644 --- a/test/functional/redirect-decorator.spec.ts +++ b/test/functional/redirect-decorator.spec.ts @@ -1,36 +1,33 @@ import "reflect-metadata"; import {Get} from "../../src/decorator/Get"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {Redirect} from "../../src/decorator/Redirect"; import {JsonController} from "../../src/decorator/JsonController"; import {Param} from "../../src/decorator/Param"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import {axios} from "../utilities/axios"; describe("dynamic redirect", function () { + let expressServer: HttpServer; - before(() => { - - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @JsonController("/users") class TestController { - @Get("/:id") async getOne(@Param("id") id: string) { return { login: id }; } - - } @JsonController() class RedirectController { - @Get("/template") @Redirect("/users/:owner") template() { @@ -47,43 +44,37 @@ describe("dynamic redirect", function () { override() { return "/users/pleerock"; } - } - }); - let expressApp: any; - before(done => { - const server = createExpressServer(); - expressApp = server.listen(3001, done); + expressServer = createExpressServer().listen(3001, done); }); - after(done => expressApp.close(done)); - let koaApp: any; - before(done => { - const server = createKoaServer(); - koaApp = server.listen(3002, done); - }); - after(done => koaApp.close(done)); + afterAll((done: DoneCallback) => expressServer.close(done)); - describe("using template", () => { - assertRequest([3001, 3002], "get", "template", response => { - expect(response).to.have.status(200); - expect(response.body).has.property("login", "pleerock"); - }); + it("using template", () => { + expect.assertions(2); + return axios.get("/template") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data.login).toEqual("pleerock"); + }); }); - describe("using override", () => { - assertRequest([3001, 3002], "get", "override", response => { - expect(response).to.have.status(200); - expect(response.body).has.property("login", "pleerock"); - }); + it("using override", () => { + expect.assertions(2); + return axios.get("/override") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data.login).toEqual("pleerock"); + }); }); - describe("using original", () => { - assertRequest([3001, 3002], "get", "original", response => { - expect(response).to.have.status(200); - expect(response.body).has.property("login", "pleerock"); - }); + it("using original", () => { + expect.assertions(2); + return axios.get("/original") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data.login).toEqual("pleerock"); + }); }); - -}); \ No newline at end of file +}); diff --git a/test/functional/render-decorator.spec.ts b/test/functional/render-decorator.spec.ts index 662c2e2a..a0c6ae5d 100644 --- a/test/functional/render-decorator.spec.ts +++ b/test/functional/render-decorator.spec.ts @@ -2,22 +2,25 @@ import "reflect-metadata"; import {Controller} from "../../src/decorator/Controller"; import {Get} from "../../src/decorator/Get"; import {Res} from "../../src/decorator/Res"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {Render} from "../../src/decorator/Render"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import express, {Application as ExpressApplication} from "express"; +import mustacheExpress from "mustache-express"; +import path from "path"; +import {axios} from "../utilities/axios"; describe("template rendering", () => { + let expressServer: HttpServer; - before(() => { - - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @Controller() class RenderController { - @Get("/index") @Render("render-test-spec.html") index() { @@ -35,54 +38,43 @@ describe("template rendering", () => { name: "Routing-controllers" }; } - } - }); - let expressApp: any; - before(done => { - const path = __dirname + "/../resources"; - const server = createExpressServer(); - const mustacheExpress = require("mustache-express"); - server.engine("html", mustacheExpress()); - server.set("view engine", "html"); - server.set("views", path); - server.use(require("express").static(path)); - expressApp = server.listen(3001, done); + const resourcePath: string = path.resolve(__dirname, "../resources"); + let expressApplication: ExpressApplication = createExpressServer(); + expressApplication.engine("html", mustacheExpress()); + expressApplication.set("view engine", "html"); + expressApplication.set("views", resourcePath); + expressApplication.use(express.static(resourcePath)); + expressServer = expressApplication.listen(3001, done); }); - after(done => expressApp.close(done)); - let koaApp: any; - before(done => { - const path = __dirname + "/../resources"; - const server = createKoaServer(); - let koaViews = require("koa-views"); - server.use(koaViews(path, { map: { html: "handlebars" } } )); - koaApp = server.listen(3002, done); - }); - after(done => koaApp.close(done)); + afterAll((done: DoneCallback) => expressServer.close(done)); - describe("should render a template and use given variables", () => { - assertRequest([3001, 3002], "get", "index", response => { - expect(response).to.have.status(200); - expect(response.body).to.contain(""); - expect(response.body).to.contain(""); - expect(response.body).to.contain("Routing-controllers"); - expect(response.body).to.contain(""); - expect(response.body).to.contain(""); - }); + it("should render a template and use given variables", () => { + expect.assertions(6); + return axios.get("/index") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toContain(""); + expect(response.data).toContain(""); + expect(response.data).toContain("Routing-controllers"); + expect(response.data).toContain(""); + expect(response.data).toContain(""); + }); }); - describe("Express should render a template with given variables and locals variables", () => { - assertRequest([3001], "get", "locals", response => { - expect(response).to.have.status(200); - expect(response.body).to.contain(""); - expect(response.body).to.contain(""); - expect(response.body).to.contain("Routing-controllers"); - expect(response.body).to.contain("my-variable"); - expect(response.body).to.contain(""); - expect(response.body).to.contain(""); - }); + it("should render a template with given variables and locals variables", () => { + expect.assertions(7); + return axios.get("/locals") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toContain(""); + expect(response.data).toContain(""); + expect(response.data).toContain("Routing-controllers"); + expect(response.data).toContain("my-variable"); + expect(response.data).toContain(""); + expect(response.data).toContain(""); + }); }); - }); diff --git a/test/functional/special-result-send.spec.ts b/test/functional/special-result-send.spec.ts index 84f06c26..5d760216 100644 --- a/test/functional/special-result-send.spec.ts +++ b/test/functional/special-result-send.spec.ts @@ -1,83 +1,76 @@ import "reflect-metadata"; - import {createReadStream} from "fs"; import * as path from "path"; -import {createExpressServer, createKoaServer, getMetadataArgsStorage} from "../../src/index"; -import {assertRequest} from "./test-utils"; -import {InterceptorInterface} from "../../src/InterceptorInterface"; -import {Interceptor} from "../../src/decorator/Interceptor"; -import {UseInterceptor} from "../../src/decorator/UseInterceptor"; +import {createExpressServer, getMetadataArgsStorage} from "../../src/index"; import {JsonController} from "../../src/decorator/JsonController"; import {Get} from "../../src/decorator/Get"; -import {Action} from "../../src/Action"; import {ContentType} from "../../src/decorator/ContentType"; -const chakram = require("chakram"); -const expect = chakram.expect; +import {AxiosResponse} from "axios"; +import {Server as HttpServer} from "http"; +import HttpStatusCodes from "http-status-codes"; +import DoneCallback = jest.DoneCallback; +import {axios} from "../utilities/axios"; describe("special result value treatment", () => { - + let expressServer: HttpServer; const rawData = [0xFF, 0x66, 0xAA, 0xCC]; - before(() => { - - // reset metadata args storage + beforeAll((done: DoneCallback) => { getMetadataArgsStorage().reset(); @JsonController() class HandledController { - @Get("/stream") @ContentType("text/plain") getStream() { - return createReadStream(path.resolve(__dirname, "../resources/sample-text-file.txt")); + return createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt")); } - + @Get("/buffer") @ContentType("application/octet-stream") getBuffer() { - return new Buffer(rawData); + return Buffer.from(rawData); } - + @Get("/array") @ContentType("application/octet-stream") getUIntArray() { return new Uint8Array(rawData); } - } + expressServer = createExpressServer().listen(3001, done); }); - let expressApp: any, koaApp: any; - before(done => expressApp = createExpressServer().listen(3001, done)); - after(done => expressApp.close(done)); - before(done => koaApp = createKoaServer().listen(3002, done)); - after(done => koaApp.close(done)); + afterAll((done: DoneCallback) => expressServer.close(done)); - describe("should pipe stream to response", () => { - assertRequest([3001, 3002], "get", "stream", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", (contentType: string) => { - expect(contentType).to.match(/text\/plain/); - }); - expect(response.body).to.be.equal("Hello World!"); + it("should pipe stream to response", () => { + expect.assertions(3); + return axios.get("/stream") + .then(async (response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/plain; charset=utf-8"); + expect(response.data).toEqual("Hello World!"); }); }); - describe("should send raw binary data from Buffer", () => { - assertRequest([3001, 3002], "get", "buffer", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "application/octet-stream"); - expect(response.body).to.be.equal(new Buffer(rawData).toString()); - }); + it("should send raw binary data from Buffer", () => { + expect.assertions(3); + return axios.get("/buffer") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/octet-stream"); + expect(response.data).toEqual(Buffer.from(rawData).toString()); + }); }); - describe("should send raw binary data from UIntArray", () => { - assertRequest([3001, 3002], "get", "array", response => { - expect(response).to.be.status(200); - expect(response).to.have.header("content-type", "application/octet-stream"); - expect(response.body).to.be.equal(Buffer.from(rawData).toString()); - }); + it("should send raw binary data from UIntArray", () => { + expect.assertions(3); + return axios.get("/array") + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("application/octet-stream"); + expect(response.data).toEqual(Buffer.from(rawData).toString()); + }); }); - }); diff --git a/test/functional/test-utils.ts b/test/functional/test-utils.ts deleted file mode 100644 index a50c18b6..00000000 --- a/test/functional/test-utils.ts +++ /dev/null @@ -1,51 +0,0 @@ -const chakram = require("chakram"); - -export function assertRequest(ports: number[], method: string, route: string, callback: (response: any) => any): void; -export function assertRequest(ports: number[], method: string, route: string, dataOrOptions: any, callback: (response: any) => any): void; -export function assertRequest(ports: number[], method: string, route: string, data: any, requestOptions: any, callback: (response: any) => any): void; -export function assertRequest(ports: number[], - method: string, - route: string, - dataOrCallback: any|((response: any) => any), - dataOrRequestOptionsOrCallback?: any|((response: any) => any), - maybeCallback?: (response: any) => any): void { - const args = arguments.length; - - ports.forEach(port => { - - it("asserting port " + port, async() => { - let unhandledRejection: Error = undefined; - const captureRejection = (e: Error) => { unhandledRejection = e; }; - process.on("unhandledRejection", captureRejection); - - try { - let r; - if (args === 4) { - r = await chakram[method](`http://127.0.0.1:${port}/${route}`).then(dataOrCallback as Function); - } - else if (args === 5) { - r = await chakram[method](`http://127.0.0.1:${port}/${route}`, dataOrCallback as any).then(dataOrRequestOptionsOrCallback as Function); - } - else if (args === 6) { - r = await chakram[method](`http://127.0.0.1:${port}/${route}`, dataOrCallback as any, dataOrRequestOptionsOrCallback as any).then(maybeCallback); - } - else { - throw new Error("No assertion has been performed"); - } - - if (unhandledRejection) { - const e = new Error("There was an unhandled rejection while processing the request"); - e.stack += "\nCaused by: " + unhandledRejection.stack; - throw e; - } - - return r; - } - finally { - process.removeListener("unhandledRejection", captureRejection); - } - }); - - }); - -} \ No newline at end of file diff --git a/test/resources/sample-text-file.txt b/test/resources/sample-text-file-one.txt similarity index 100% rename from test/resources/sample-text-file.txt rename to test/resources/sample-text-file-one.txt diff --git a/test/resources/sample-text-file-three.txt b/test/resources/sample-text-file-three.txt new file mode 100644 index 00000000..c57eff55 --- /dev/null +++ b/test/resources/sample-text-file-three.txt @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/test/resources/sample-text-file-two.txt b/test/resources/sample-text-file-two.txt new file mode 100644 index 00000000..c57eff55 --- /dev/null +++ b/test/resources/sample-text-file-two.txt @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/test/utilities/axios.ts b/test/utilities/axios.ts new file mode 100644 index 00000000..fc5214ca --- /dev/null +++ b/test/utilities/axios.ts @@ -0,0 +1,5 @@ +import Axios, {AxiosInstance} from "axios"; + +export const axios: AxiosInstance = Axios.create({ + baseURL: "http://localhost:3001/" +}); diff --git a/tsconfig.json b/tsconfig.json index afdeb978..c7004613 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,69 @@ { - "version": "1.8.0", - "compilerOptions": { - "outDir": "build/compiled", - "lib": ["es6"], - "target": "es5", - "module": "commonjs", - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "sourceMap": true, - "noImplicitAny": true, - "declaration": true - }, - "exclude": [ - "build", - "node_modules" - ] + // http://www.typescriptlang.org/docs/handbook/compiler-options.html + // ./test included only for eslint + "include": ["./src", "./test"], + "compilerOptions": { + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "lib": ["ES2015"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": false, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } } diff --git a/tslint.json b/tslint.json deleted file mode 100644 index d6ce3123..00000000 --- a/tslint.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "no-duplicate-variable": true, - "no-eval": true, - "no-internal-module": true, - "no-var-keyword": true, - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "quotemark": [ - true, - "double" - ], - "semicolon": [true], - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "variable-name": [ - true, - "ban-keywords" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] - } -} \ No newline at end of file From 6257ac43f4583eb896121c9e86570d821a2c9cdf Mon Sep 17 00:00:00 2001 From: Rob Muchall Date: Wed, 1 Apr 2020 23:37:15 +0100 Subject: [PATCH 2/4] Added eslint --- .eslintrc.js | 32 + README.md | 12 +- package-lock.json | 682 +++++++++++++++++- package.json | 9 +- src/ActionParameterHandler.ts | 6 +- src/RoutingControllers.ts | 6 +- src/RoutingControllersOptions.ts | 7 +- src/decorator/Authorized.ts | 4 +- src/decorator/Body.ts | 5 +- src/decorator/BodyParam.ts | 4 +- src/decorator/ContentType.ts | 4 +- src/decorator/Controller.ts | 4 +- src/decorator/CookieParam.ts | 4 +- src/decorator/CookieParams.ts | 4 +- src/decorator/CurrentUser.ts | 4 +- src/decorator/Delete.ts | 2 +- src/decorator/Get.ts | 4 +- src/decorator/Head.ts | 4 +- src/decorator/Header.ts | 4 +- src/decorator/HeaderParam.ts | 4 +- src/decorator/HeaderParams.ts | 4 +- src/decorator/HttpCode.ts | 2 +- src/decorator/Interceptor.ts | 2 +- src/decorator/JsonController.ts | 2 +- src/decorator/Location.ts | 4 +- src/decorator/Method.ts | 2 +- src/decorator/Middleware.ts | 4 +- src/decorator/OnNull.ts | 2 +- src/decorator/OnUndefined.ts | 2 +- src/decorator/Param.ts | 4 +- src/decorator/Params.ts | 2 +- src/decorator/Patch.ts | 4 +- src/decorator/Post.ts | 2 +- src/decorator/Put.ts | 2 +- src/decorator/QueryParam.ts | 4 +- src/decorator/QueryParams.ts | 4 +- src/decorator/Redirect.ts | 2 +- src/decorator/Render.ts | 4 +- src/decorator/Req.ts | 4 +- src/decorator/Res.ts | 2 +- .../ResponseClassTransformOptions.ts | 2 +- src/decorator/Session.ts | 2 +- src/decorator/SessionParam.ts | 2 +- src/decorator/State.ts | 5 +- src/decorator/UploadedFile.ts | 4 +- src/decorator/UploadedFiles.ts | 4 +- src/decorator/UseAfter.ts | 2 +- src/decorator/UseBefore.ts | 2 +- src/decorator/UseInterceptor.ts | 2 +- src/driver/BaseDriver.ts | 85 ++- src/driver/express/ExpressDriver.ts | 51 +- src/index.ts | 55 +- src/metadata-builder/MetadataArgsStorage.ts | 4 +- src/metadata-builder/MetadataBuilder.ts | 29 +- src/metadata/ActionMetadata.ts | 82 +-- src/metadata/ControllerMetadata.ts | 2 +- src/typings/template-url.d.ts | 1 + src/util/container.ts | 2 +- src/util/importClassesFromDirectories.ts | 6 +- test/fakes/global-options/FakeService.ts | 12 +- .../first-controllers/post/PostController.ts | 6 +- .../question/AnswerController.ts | 6 +- .../question/QuestionController.ts | 6 +- .../second-controllers/PhotoController.ts | 6 +- .../second-controllers/VideoController.ts | 6 +- test/functional/action-params.spec.ts | 32 +- test/functional/auth-decorator.spec.ts | 32 +- test/functional/container.spec.ts | 2 +- .../functional/controller-base-routes.spec.ts | 8 +- test/functional/controller-methods.spec.ts | 26 +- test/functional/defaults.spec.ts | 12 +- .../express-custom-error-handling.spec.ts | 4 +- .../functional/express-error-handling.spec.ts | 16 +- ...press-global-before-error-handling.spec.ts | 5 +- test/functional/express-middlewares.spec.ts | 12 +- test/functional/global-options.spec.ts | 4 +- .../json-controller-methods.spec.ts | 26 +- test/functional/middlewares-order.spec.ts | 4 +- .../other-controller-decorators.spec.ts | 20 +- test/functional/redirect-decorator.spec.ts | 9 +- test/functional/render-decorator.spec.ts | 6 +- test/functional/special-result-send.spec.ts | 17 +- 82 files changed, 1097 insertions(+), 385 deletions(-) create mode 100644 .eslintrc.js create mode 100644 src/typings/template-url.d.ts diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..c080459f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,32 @@ +module.exports = { + root: true, + env: { + node: true + }, + parser: "@typescript-eslint/parser", + parserOptions: { + project: ['./tsconfig.json'] + }, + plugins: [ + "@typescript-eslint" + ], + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + ignorePatterns: ["**/*.js"], + rules: { + "no-case-declarations": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/member-delimiter-style": "off", + "@typescript-eslint/member-ordering": "error", + "@typescript-eslint/unbound-method": ["error", { + "ignoreStatic": true + }] + } +}; diff --git a/README.md b/README.md index 898c8fa2..f32e85e6 100644 --- a/README.md +++ b/README.md @@ -733,7 +733,7 @@ There are set of prepared errors you can use: * UnauthorizedError -You can also create and use your own errors by extending `HttpError` class. +You can also create and use your own errors by extending `HttpError` class. To define the data returned to the client, you could define a toJSON method in your error. ```typescript @@ -755,7 +755,7 @@ class DbError extends HttpError { } } } -``` +``` #### Enable CORS @@ -796,7 +796,7 @@ app.listen(3000); #### Default settings -You can override default status code in routing-controllers options. +You can override default status code in routing-controllers options. ```typescript import "reflect-metadata"; @@ -809,9 +809,9 @@ const app = createExpressServer({ //with this option, null will return 404 by default nullResultCode: 404, - //with this option, void or Promise will return 204 by default + //with this option, void or Promise will return 204 by default undefinedResultCode: 204, - + paramOptions: { //with this option, argument will be required by default required: true @@ -1022,7 +1022,7 @@ import {Middleware, ExpressMiddlewareInterface} from "routing-controllers"; @Middleware({ type: "before" }) export class LoggingMiddleware implements ExpressMiddlewareInterface { - use(request: any, response: any, next: (err: any) => any): void { + use(error: any, request: express.Request, response: express.Response, next: express.NextFunction): any: void { console.log("do something..."); next(); } diff --git a/package-lock.json b/package-lock.json index e7f98ba4..b566edba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "routing-controllers", - "version": "0.8.0", + "version": "0.9.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -677,6 +677,12 @@ "@types/node": "*" } }, + "@types/cookie": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", + "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==", + "dev": true + }, "@types/cors": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.6.tgz", @@ -686,6 +692,12 @@ "@types/express": "*" } }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/express": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.4.tgz", @@ -762,6 +774,12 @@ "pretty-format": "^25.1.0" } }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, "@types/lru-cache": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-4.1.2.tgz", @@ -853,6 +871,74 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz", + "integrity": "sha512-4yUnLv40bzfzsXcTAtZyTjbiGUXMrcIJcIMioI22tSOyAxpdXiZ4r7YQUU8Jj6XXrLz9d5aMHPQf5JFR7h27Nw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.26.0", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.26.0.tgz", + "integrity": "sha512-RELVoH5EYd+JlGprEyojUv9HeKcZqF7nZUGSblyAw1FwOGNnmQIU8kxJ69fttQvEwCsX5D6ECJT8GTozxrDKVQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.26.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.26.0.tgz", + "integrity": "sha512-+Xj5fucDtdKEVGSh9353wcnseMRkPpEAOY96EEenN7kJVrLqy/EVwtIh3mxcUz8lsFXW1mT5nN5vvEam/a5HiQ==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.26.0", + "@typescript-eslint/typescript-estree": "2.26.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.26.0.tgz", + "integrity": "sha512-3x4SyZCLB4zsKsjuhxDLeVJN6W29VwBnYpCsZ7vIdPel9ZqLfIZJgJXO47MNUkurGpQuIBALdPQKtsSnWpE1Yg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "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 + } + } + }, "abab": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", @@ -893,6 +979,12 @@ } } }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, "acorn-walk": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", @@ -1352,6 +1444,12 @@ "supports-color": "^7.1.0" } }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -1398,6 +1496,21 @@ "validator": "12.0.0" } }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -1773,6 +1886,15 @@ "integrity": "sha512-foe7dXnGlSh3jR1ovJmdv+77VQj98eKCHHwJPbZ2eEf0fHwKbkZicpPxEch9smZ+n2dnF6QFwkOQdLq9hpeJUg==", "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" + } + }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -1843,12 +1965,225 @@ "source-map": "~0.6.1" } }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "eslint-plugin-jest": { + "version": "23.8.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.8.2.tgz", + "integrity": "sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "^2.5.0" + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", + "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.2.0.tgz", + "integrity": "sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q==", + "dev": true, + "requires": { + "estraverse": "^5.0.0" + }, + "dependencies": { + "estraverse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz", + "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", @@ -2073,6 +2408,17 @@ } } }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -2171,6 +2517,24 @@ "bser": "2.1.1" } }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2215,6 +2579,34 @@ "locate-path": "^3.0.0" } }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", @@ -2279,6 +2671,12 @@ "dev": true, "optional": 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.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", @@ -2328,6 +2726,15 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -2507,6 +2914,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.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "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 + } + } + }, "import-local": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", @@ -2537,6 +2968,61 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "inquirer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "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" + } + } + } + }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -2629,6 +3115,12 @@ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -2641,6 +3133,15 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2656,6 +3157,12 @@ "isobject": "^3.0.1" } }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -3578,6 +4085,12 @@ "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 + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -3856,6 +4369,12 @@ "mustache": "^3.1.0" } }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -4105,6 +4624,12 @@ "word-wrap": "~1.2.3" } }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, "p-each-series": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", @@ -4141,6 +4666,15 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "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" + } + }, "parse5": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", @@ -4289,6 +4823,12 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "prompts": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", @@ -4399,6 +4939,12 @@ "safe-regex": "^1.1.0" } }, + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -4548,6 +5094,16 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -4569,6 +5125,24 @@ "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", "dev": true }, + "run-async": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", + "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", + "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -4878,6 +5452,43 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -5172,6 +5783,12 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -5197,6 +5814,18 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } + }, "template-url": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/template-url/-/template-url-1.0.0.tgz", @@ -5223,12 +5852,24 @@ "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 + }, "throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -5271,6 +5912,15 @@ } } }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -5399,6 +6049,21 @@ "yn": "3.1.1" } }, + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -5592,6 +6257,12 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, "v8-to-istanbul": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.3.tgz", @@ -5764,6 +6435,15 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", diff --git a/package.json b/package.json index 7295288e..6cd6d141 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ }, "devDependencies": { "@types/body-parser": "1.19.0", + "@types/cookie": "^0.3.3", "@types/cors": "2.8.6", "@types/express": "4.17.4", "@types/express-session": "1.17.0", @@ -51,12 +52,16 @@ "@types/mustache-express": "1.2.1", "@types/node": "13.9.8", "@types/qs": "6.9.1", + "@typescript-eslint/eslint-plugin": "2.26.0", + "@typescript-eslint/parser": "2.26.0", "axios": "0.19.2", "body-parser": "1.19.0", "class-transformer": "0.2.3", "class-validator": "0.11.1", "copyfiles": "2.2.0", "cors": "2.8.5", + "eslint": "6.8.0", + "eslint-plugin-jest": "23.8.2", "express": "4.17.1", "express-session": "1.17.0", "handlebars": "4.7.3", @@ -79,10 +84,10 @@ "build": "rimraf build && echo Using TypeScript && tsc --version && tsc --pretty", "clean": "rimraf build coverage", "copy": "copyfiles -u 3 \"build/compiled/src/**/*\" build/package && copyfiles package.json README.md build/package", - "lint": "tslint --fix --format verbose --project tsconfig.json", + "lint": "eslint --config ./.eslintrc.js --ext .ts ./src ./test", "package": "npm run build && npm run copy && npm run public && rimraf build/compiled", "pretest": "npm run lint", "public": "json -I -f build/package/package.json -e 'this.private=false'", - "test": "rimraf coverage && cross-env jest --coverage" + "test": "rimraf coverage && jest --coverage" } } diff --git a/src/ActionParameterHandler.ts b/src/ActionParameterHandler.ts index 070a06ce..59c6d699 100644 --- a/src/ActionParameterHandler.ts +++ b/src/ActionParameterHandler.ts @@ -142,7 +142,7 @@ export class ActionParameterHandler { } // if target type is not primitive, transform and validate it - if ((["number", "string", "boolean"].indexOf(param.targetName) === -1) + if ((!["number", "string", "boolean"].includes(param.targetName)) && (param.parse || param.isTargetObject) ) { value = this.parseValue(value, param); @@ -156,7 +156,7 @@ export class ActionParameterHandler { /** * Normalizes string value to number or boolean. */ - protected normalizeStringValue(value: string, parameterName: string, parameterType: string) { + protected normalizeStringValue(value: string, parameterName: string, parameterType: string): any { switch (parameterType) { case "number": if (value === "") { @@ -164,7 +164,7 @@ export class ActionParameterHandler { } const valueNumber = +value; - if (valueNumber === NaN) { + if (isNaN(valueNumber)) { throw new InvalidParamError(value, parameterName, parameterType); } diff --git a/src/RoutingControllers.ts b/src/RoutingControllers.ts index 033f8abc..b5121e83 100644 --- a/src/RoutingControllers.ts +++ b/src/RoutingControllers.ts @@ -108,7 +108,7 @@ export class RoutingControllers { /** * Executes given controller action. */ - protected executeAction(actionMetadata: ActionMetadata, action: Action, interceptorFns: Function[]) { + protected executeAction(actionMetadata: ActionMetadata, action: Action, interceptorFns: Function[]): any { // compute all parameters const paramsPromises = actionMetadata.params @@ -170,7 +170,9 @@ export class RoutingControllers { protected prepareInterceptors(uses: InterceptorMetadata[]): Function[] { return uses.map(use => { if (use.interceptor.prototype && use.interceptor.prototype.intercept) { // if this is function instance of InterceptorInterface - return function (action: Action, result: any) { + return function (action: Action, result: any): any { + // TODO: Fix this rule + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion return (getFromContainer(use.interceptor) as InterceptorInterface).intercept(action, result); }; } diff --git a/src/RoutingControllersOptions.ts b/src/RoutingControllersOptions.ts index 82ba2194..295a55f6 100644 --- a/src/RoutingControllersOptions.ts +++ b/src/RoutingControllersOptions.ts @@ -1,7 +1,6 @@ import {AuthorizationChecker} from "./AuthorizationChecker"; import {ClassTransformOptions} from "class-transformer"; import {CurrentUserChecker} from "./CurrentUserChecker"; -import { ParamOptions } from "./decorator-options/ParamOptions"; import {ValidatorOptions} from "class-validator"; /** @@ -13,7 +12,7 @@ export interface RoutingControllersOptions { * Indicates if cors are enabled. * This requires installation of additional module (cors for express and kcors for koa). */ - cors?: boolean|Object; + cors?: boolean | Record; /** * Global route prefix, for example '/api'. @@ -85,7 +84,7 @@ export interface RoutingControllersOptions { * Special function used to get currently authorized user. */ currentUserChecker?: CurrentUserChecker; - + /** * Default settings */ @@ -110,4 +109,4 @@ export interface RoutingControllersOptions { required?: boolean; }; }; -} \ No newline at end of file +} diff --git a/src/decorator/Authorized.ts b/src/decorator/Authorized.ts index 55066c68..42256029 100644 --- a/src/decorator/Authorized.ts +++ b/src/decorator/Authorized.ts @@ -29,7 +29,7 @@ export function Authorized(role: Function): Function; * Authorization logic must be defined in routing-controllers settings. */ export function Authorized(roleOrRoles?: string|string[]|Function): Function { - return function (clsOrObject: Function|Object, method?: string) { + return function (clsOrObject: Function | Record, method?: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "authorized", target: method ? clsOrObject.constructor : clsOrObject as Function, @@ -37,4 +37,4 @@ export function Authorized(roleOrRoles?: string|string[]|Function): Function { value: roleOrRoles }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Body.ts b/src/decorator/Body.ts index 208e096e..a0ba9694 100644 --- a/src/decorator/Body.ts +++ b/src/decorator/Body.ts @@ -6,7 +6,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function Body(options?: BodyOptions): Function { - return function (object: Object, methodName: string, index: number) { + return function (object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "body", object: object, @@ -20,4 +20,5 @@ export function Body(options?: BodyOptions): Function { extraOptions: options ? options.options : undefined }); }; -} \ No newline at end of file +} + diff --git a/src/decorator/BodyParam.ts b/src/decorator/BodyParam.ts index 973275c1..e8e97b1d 100644 --- a/src/decorator/BodyParam.ts +++ b/src/decorator/BodyParam.ts @@ -6,7 +6,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function BodyParam(name: string, options?: ParamOptions): Function { - return function (object: Object, methodName: string, index: number) { + return function (object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "body-param", object: object, @@ -20,4 +20,4 @@ export function BodyParam(name: string, options?: ParamOptions): Function { validate: options ? options.validate : undefined }); }; -} \ No newline at end of file +} diff --git a/src/decorator/ContentType.ts b/src/decorator/ContentType.ts index 64f1624e..9d40df59 100644 --- a/src/decorator/ContentType.ts +++ b/src/decorator/ContentType.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action. */ export function ContentType(contentType: string): Function { - return function (object: Object, methodName: string) { + return function (object: Record, methodName: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "content-type", target: object.constructor, @@ -13,4 +13,4 @@ export function ContentType(contentType: string): Function { value: contentType }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Controller.ts b/src/decorator/Controller.ts index 59857a97..11a97ef1 100644 --- a/src/decorator/Controller.ts +++ b/src/decorator/Controller.ts @@ -8,11 +8,11 @@ import {getMetadataArgsStorage} from "../index"; * @param baseRoute Extra path you can apply as a base route to all controller actions */ export function Controller(baseRoute?: string): Function { - return function (object: Function) { + return function(object: Function): void { getMetadataArgsStorage().controllers.push({ type: "default", target: object, route: baseRoute }); }; -} \ No newline at end of file +} diff --git a/src/decorator/CookieParam.ts b/src/decorator/CookieParam.ts index 00faf391..d2af17a1 100644 --- a/src/decorator/CookieParam.ts +++ b/src/decorator/CookieParam.ts @@ -6,7 +6,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function CookieParam(name: string, options?: ParamOptions) { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "cookie", object: object, @@ -20,4 +20,4 @@ export function CookieParam(name: string, options?: ParamOptions) { validate: options ? options.validate : undefined }); }; -} \ No newline at end of file +} diff --git a/src/decorator/CookieParams.ts b/src/decorator/CookieParams.ts index df0cb05d..0866000a 100644 --- a/src/decorator/CookieParams.ts +++ b/src/decorator/CookieParams.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function CookieParams() { - return function (object: Object, methodName: string, index: number) { + return function (object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "cookies", object: object, @@ -15,4 +15,4 @@ export function CookieParams() { required: false }); }; -} \ No newline at end of file +} diff --git a/src/decorator/CurrentUser.ts b/src/decorator/CurrentUser.ts index 9f1d87ba..bf9019ae 100644 --- a/src/decorator/CurrentUser.ts +++ b/src/decorator/CurrentUser.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Authorization logic must be defined in routing-controllers settings. */ export function CurrentUser(options?: { required?: boolean }) { - return function (object: Object, methodName: string, index: number) { + return function (object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "current-user", object: object, @@ -15,4 +15,4 @@ export function CurrentUser(options?: { required?: boolean }) { required: options ? options.required : undefined }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Delete.ts b/src/decorator/Delete.ts index f0f0fcd4..11dda9df 100644 --- a/src/decorator/Delete.ts +++ b/src/decorator/Delete.ts @@ -17,7 +17,7 @@ export function Delete(route?: string): Function; * Must be applied on a controller action. */ export function Delete(route?: string|RegExp): Function { - return function (object: Object, methodName: string) { + return function (object: Record, methodName: string): void { getMetadataArgsStorage().actions.push({ type: "delete", target: object.constructor, diff --git a/src/decorator/Get.ts b/src/decorator/Get.ts index 36b8ffe8..93765caa 100644 --- a/src/decorator/Get.ts +++ b/src/decorator/Get.ts @@ -17,7 +17,7 @@ export function Get(route?: string): Function; * Must be applied on a controller action. */ export function Get(route?: string|RegExp): Function { - return function (object: Object, methodName: string) { + return function (object: Record, methodName: string): void { getMetadataArgsStorage().actions.push({ type: "get", target: object.constructor, @@ -25,4 +25,4 @@ export function Get(route?: string|RegExp): Function { route: route }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Head.ts b/src/decorator/Head.ts index 4a2e96c8..e6dc8874 100644 --- a/src/decorator/Head.ts +++ b/src/decorator/Head.ts @@ -17,7 +17,7 @@ export function Head(route?: string): Function; * Must be applied on a controller action. */ export function Head(route?: string|RegExp): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().actions.push({ type: "head", target: object.constructor, @@ -25,4 +25,4 @@ export function Head(route?: string|RegExp): Function { route: route }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Header.ts b/src/decorator/Header.ts index f5cb7b16..055725a4 100644 --- a/src/decorator/Header.ts +++ b/src/decorator/Header.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action. */ export function Header(name: string, value: string): Function { - return function (object: Object, methodName: string) { + return function (object: Record, methodName: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "header", target: object.constructor, @@ -14,4 +14,4 @@ export function Header(name: string, value: string): Function { secondaryValue: value }); }; -} \ No newline at end of file +} diff --git a/src/decorator/HeaderParam.ts b/src/decorator/HeaderParam.ts index 5a40e08e..e00ae9be 100644 --- a/src/decorator/HeaderParam.ts +++ b/src/decorator/HeaderParam.ts @@ -6,7 +6,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function HeaderParam(name: string, options?: ParamOptions): Function { - return function (object: Object, methodName: string, index: number) { + return function (object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "header", object: object, @@ -20,4 +20,4 @@ export function HeaderParam(name: string, options?: ParamOptions): Function { validate: options ? options.validate : undefined }); }; -} \ No newline at end of file +} diff --git a/src/decorator/HeaderParams.ts b/src/decorator/HeaderParams.ts index 6a2abd51..be5d349e 100644 --- a/src/decorator/HeaderParams.ts +++ b/src/decorator/HeaderParams.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function HeaderParams(): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "headers", object: object, @@ -15,4 +15,4 @@ export function HeaderParams(): Function { required: false }); }; -} \ No newline at end of file +} diff --git a/src/decorator/HttpCode.ts b/src/decorator/HttpCode.ts index f4a18b10..bc0bbbdb 100644 --- a/src/decorator/HttpCode.ts +++ b/src/decorator/HttpCode.ts @@ -7,7 +7,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action. */ export function HttpCode(code: number): Function { - return function (object: Object, methodName: string) { + return function (object: Record, methodName: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "success-code", target: object.constructor, diff --git a/src/decorator/Interceptor.ts b/src/decorator/Interceptor.ts index a1386ed8..79a767c9 100644 --- a/src/decorator/Interceptor.ts +++ b/src/decorator/Interceptor.ts @@ -4,7 +4,7 @@ import {getMetadataArgsStorage} from "../index"; * Registers a global interceptor. */ export function Interceptor(options?: { priority?: number }): Function { - return function (target: Function) { + return function(target: Function): void { getMetadataArgsStorage().interceptors.push({ target: target, global: true, diff --git a/src/decorator/JsonController.ts b/src/decorator/JsonController.ts index 826c1d4b..d7f79fb5 100644 --- a/src/decorator/JsonController.ts +++ b/src/decorator/JsonController.ts @@ -7,7 +7,7 @@ import {getMetadataArgsStorage} from "../index"; * @param baseRoute Extra path you can apply as a base route to all controller actions */ export function JsonController(baseRoute?: string) { - return function (object: Function) { + return function(object: Function): void { getMetadataArgsStorage().controllers.push({ type: "json", target: object, diff --git a/src/decorator/Location.ts b/src/decorator/Location.ts index 3e5ba0fb..858ae235 100644 --- a/src/decorator/Location.ts +++ b/src/decorator/Location.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action. */ export function Location(url: string): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "location", target: object.constructor, @@ -13,4 +13,4 @@ export function Location(url: string): Function { value: url }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Method.ts b/src/decorator/Method.ts index cb9f8bbd..6ba157b0 100644 --- a/src/decorator/Method.ts +++ b/src/decorator/Method.ts @@ -18,7 +18,7 @@ export function Method(method: ActionType, route?: string): Function; * Must be applied on a controller action. */ export function Method(method: ActionType, route?: string|RegExp): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().actions.push({ type: method, target: object.constructor, diff --git a/src/decorator/Middleware.ts b/src/decorator/Middleware.ts index 797877fe..062ad7e9 100644 --- a/src/decorator/Middleware.ts +++ b/src/decorator/Middleware.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Allows to create global middlewares and control order of middleware execution. */ export function Middleware(options: { type: "after"|"before", priority?: number }): Function { - return function (target: Function) { + return function(target: Function): void { getMetadataArgsStorage().middlewares.push({ target: target, type: options && options.type ? options.type : "before", @@ -13,4 +13,4 @@ export function Middleware(options: { type: "after"|"before", priority?: number priority: options && options.priority !== undefined ? options.priority : 0 }); }; -} \ No newline at end of file +} diff --git a/src/decorator/OnNull.ts b/src/decorator/OnNull.ts index c747144f..42f8294e 100644 --- a/src/decorator/OnNull.ts +++ b/src/decorator/OnNull.ts @@ -17,7 +17,7 @@ export function OnNull(error: Function): Function; * Must be applied on a controller action. */ export function OnNull(codeOrError: number|Function): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "on-null", target: object.constructor, diff --git a/src/decorator/OnUndefined.ts b/src/decorator/OnUndefined.ts index bc203b38..0cdb73bf 100644 --- a/src/decorator/OnUndefined.ts +++ b/src/decorator/OnUndefined.ts @@ -17,7 +17,7 @@ export function OnUndefined(error: Function): Function; * Must be applied on a controller action. */ export function OnUndefined(codeOrError: number|Function): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "on-undefined", target: object.constructor, diff --git a/src/decorator/Param.ts b/src/decorator/Param.ts index cc176888..765307ae 100644 --- a/src/decorator/Param.ts +++ b/src/decorator/Param.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function Param(name: string): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "param", object: object, @@ -17,4 +17,4 @@ export function Param(name: string): Function { classTransform: undefined }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Params.ts b/src/decorator/Params.ts index 261e9159..d304a532 100644 --- a/src/decorator/Params.ts +++ b/src/decorator/Params.ts @@ -6,7 +6,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function Params(options?: ParamOptions): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "params", object: object, diff --git a/src/decorator/Patch.ts b/src/decorator/Patch.ts index 2246e5ed..6a1b3c0e 100644 --- a/src/decorator/Patch.ts +++ b/src/decorator/Patch.ts @@ -17,7 +17,7 @@ export function Patch(route?: string): Function; * Must be applied on a controller action. */ export function Patch(route?: string|RegExp): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().actions.push({ type: "patch", target: object.constructor, @@ -25,4 +25,4 @@ export function Patch(route?: string|RegExp): Function { route: route }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Post.ts b/src/decorator/Post.ts index 87b774bb..d4eacd80 100644 --- a/src/decorator/Post.ts +++ b/src/decorator/Post.ts @@ -17,7 +17,7 @@ export function Post(route?: string): Function; * Must be applied on a controller action. */ export function Post(route?: string|RegExp): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().actions.push({ type: "post", target: object.constructor, diff --git a/src/decorator/Put.ts b/src/decorator/Put.ts index 5bbd19d4..ec843077 100644 --- a/src/decorator/Put.ts +++ b/src/decorator/Put.ts @@ -17,7 +17,7 @@ export function Put(route?: string): Function; * Must be applied on a controller action. */ export function Put(route?: string|RegExp): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().actions.push({ type: "put", target: object.constructor, diff --git a/src/decorator/QueryParam.ts b/src/decorator/QueryParam.ts index 2c7b91da..6191d949 100644 --- a/src/decorator/QueryParam.ts +++ b/src/decorator/QueryParam.ts @@ -6,7 +6,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function QueryParam(name: string, options?: ParamOptions): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "query", object: object, @@ -20,4 +20,4 @@ export function QueryParam(name: string, options?: ParamOptions): Function { validate: options ? options.validate : undefined }); }; -} \ No newline at end of file +} diff --git a/src/decorator/QueryParams.ts b/src/decorator/QueryParams.ts index 47297a7e..c17e3059 100644 --- a/src/decorator/QueryParams.ts +++ b/src/decorator/QueryParams.ts @@ -6,7 +6,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function QueryParams(options?: ParamOptions): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "queries", object: object, @@ -20,4 +20,4 @@ export function QueryParams(options?: ParamOptions): Function { validate: options ? options.validate : undefined, }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Redirect.ts b/src/decorator/Redirect.ts index f12411d1..4a908548 100644 --- a/src/decorator/Redirect.ts +++ b/src/decorator/Redirect.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action. */ export function Redirect(url: string): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "redirect", target: object.constructor, diff --git a/src/decorator/Render.ts b/src/decorator/Render.ts index cca41aae..6af199ae 100644 --- a/src/decorator/Render.ts +++ b/src/decorator/Render.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action. */ export function Render(template: string): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "rendered-template", target: object.constructor, @@ -13,4 +13,4 @@ export function Render(template: string): Function { value: template }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Req.ts b/src/decorator/Req.ts index 228922d0..7d29606b 100644 --- a/src/decorator/Req.ts +++ b/src/decorator/Req.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function Req(): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "request", object: object, @@ -15,4 +15,4 @@ export function Req(): Function { required: false }); }; -} \ No newline at end of file +} diff --git a/src/decorator/Res.ts b/src/decorator/Res.ts index a5cf7a06..9be2f121 100644 --- a/src/decorator/Res.ts +++ b/src/decorator/Res.ts @@ -5,7 +5,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function Res(): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "response", object: object, diff --git a/src/decorator/ResponseClassTransformOptions.ts b/src/decorator/ResponseClassTransformOptions.ts index e9103b0b..37e625aa 100644 --- a/src/decorator/ResponseClassTransformOptions.ts +++ b/src/decorator/ResponseClassTransformOptions.ts @@ -5,7 +5,7 @@ import {ClassTransformOptions} from "class-transformer"; * Options to be set to class-transformer for the result of the response. */ export function ResponseClassTransformOptions(options: ClassTransformOptions): Function { - return function (object: Object, methodName: string) { + return function(object: Record, methodName: string): void { getMetadataArgsStorage().responseHandlers.push({ type: "response-class-transform-options", value: options, diff --git a/src/decorator/Session.ts b/src/decorator/Session.ts index 044a0d92..3af40185 100644 --- a/src/decorator/Session.ts +++ b/src/decorator/Session.ts @@ -6,7 +6,7 @@ import { getMetadataArgsStorage } from "../index"; * Must be applied on a controller action parameter. */ export function Session(options?: ParamOptions): ParameterDecorator { - return function (object: Object, methodName: string | symbol, index: number) { + return function(object: Record, methodName: string | symbol, index: number): void { getMetadataArgsStorage().params.push({ type: "session", object: object, diff --git a/src/decorator/SessionParam.ts b/src/decorator/SessionParam.ts index bf972a49..2f15025d 100644 --- a/src/decorator/SessionParam.ts +++ b/src/decorator/SessionParam.ts @@ -6,7 +6,7 @@ import { getMetadataArgsStorage } from "../index"; * Must be applied on a controller action parameter. */ export function SessionParam(propertyName: string, options?: ParamOptions): ParameterDecorator { - return function (object: Object, methodName: string | symbol, index: number) { + return function(object: Record, methodName: string | symbol, index: number): void { getMetadataArgsStorage().params.push({ type: "session-param", object: object, diff --git a/src/decorator/State.ts b/src/decorator/State.ts index 392ce0a5..b987d262 100644 --- a/src/decorator/State.ts +++ b/src/decorator/State.ts @@ -4,8 +4,9 @@ import {getMetadataArgsStorage} from "../index"; * Injects a State object to the controller action parameter. * Must be applied on a controller action parameter. */ +// TODO: Is there a test for this? export function State(objectName?: string): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "state", object: object, @@ -17,4 +18,4 @@ export function State(objectName?: string): Function { classTransform: undefined }); }; -} \ No newline at end of file +} diff --git a/src/decorator/UploadedFile.ts b/src/decorator/UploadedFile.ts index 73207be6..4d924380 100644 --- a/src/decorator/UploadedFile.ts +++ b/src/decorator/UploadedFile.ts @@ -6,7 +6,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function UploadedFile(name: string, options?: UploadOptions): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "file", object: object, @@ -18,4 +18,4 @@ export function UploadedFile(name: string, options?: UploadOptions): Function { extraOptions: options ? options.options : undefined }); }; -} \ No newline at end of file +} diff --git a/src/decorator/UploadedFiles.ts b/src/decorator/UploadedFiles.ts index cc2c2d02..54e04ffa 100644 --- a/src/decorator/UploadedFiles.ts +++ b/src/decorator/UploadedFiles.ts @@ -6,7 +6,7 @@ import {getMetadataArgsStorage} from "../index"; * Must be applied on a controller action parameter. */ export function UploadedFiles(name: string, options?: UploadOptions): Function { - return function (object: Object, methodName: string, index: number) { + return function(object: Record, methodName: string, index: number): void { getMetadataArgsStorage().params.push({ type: "files", object: object, @@ -18,4 +18,4 @@ export function UploadedFiles(name: string, options?: UploadOptions): Function { extraOptions: options ? options.options : undefined }); }; -} \ No newline at end of file +} diff --git a/src/decorator/UseAfter.ts b/src/decorator/UseAfter.ts index 720a601f..ab5cd1a2 100644 --- a/src/decorator/UseAfter.ts +++ b/src/decorator/UseAfter.ts @@ -23,7 +23,7 @@ export function UseAfter(...middlewares: Array<(request: any, response: any, nex * Must be set to controller action or controller class. */ export function UseAfter(...middlewares: Array any)>): Function { - return function (objectOrFunction: Object|Function, methodName?: string) { + return function(objectOrFunction: Record | Function, methodName?: string): void { middlewares.forEach(middleware => { getMetadataArgsStorage().uses.push({ target: methodName ? objectOrFunction.constructor : objectOrFunction as Function, diff --git a/src/decorator/UseBefore.ts b/src/decorator/UseBefore.ts index b9f0afa1..a98909a5 100644 --- a/src/decorator/UseBefore.ts +++ b/src/decorator/UseBefore.ts @@ -23,7 +23,7 @@ export function UseBefore(...middlewares: Array<(request: any, response: any, ne * Must be set to controller action or controller class. */ export function UseBefore(...middlewares: Array any)>): Function { - return function (objectOrFunction: Object|Function, methodName?: string) { + return function(objectOrFunction: Record | Function, methodName?: string): void { middlewares.forEach(middleware => { getMetadataArgsStorage().uses.push({ target: methodName ? objectOrFunction.constructor : objectOrFunction as Function, diff --git a/src/decorator/UseInterceptor.ts b/src/decorator/UseInterceptor.ts index 0e4ff7cb..8d9e6d52 100644 --- a/src/decorator/UseInterceptor.ts +++ b/src/decorator/UseInterceptor.ts @@ -18,7 +18,7 @@ export function UseInterceptor(...interceptors: Array<(action: Action, result: a * Must be set to controller action or controller class. */ export function UseInterceptor(...interceptors: Array any)>): Function { - return function (objectOrFunction: Object|Function, methodName?: string) { + return function(objectOrFunction: Record | Function, methodName?: string): void { interceptors.forEach(interceptor => { getMetadataArgsStorage().useInterceptors.push({ interceptor: interceptor, diff --git a/src/driver/BaseDriver.ts b/src/driver/BaseDriver.ts index 26c298db..ed9308ef 100644 --- a/src/driver/BaseDriver.ts +++ b/src/driver/BaseDriver.ts @@ -44,7 +44,7 @@ export abstract class BaseDriver { * Global class-validator options passed during validate operation. */ validationOptions: ValidatorOptions; - + /** * Global class transformer options passed to class-transformer during plainToClass operation. * This operation is being executed when parsing user parameters. @@ -70,7 +70,7 @@ export abstract class BaseDriver { * Indicates if cors are enabled. * This requires installation of additional module (cors for express and kcors for koa). */ - cors?: boolean|Object; + cors?: boolean | Record; /** * Map of error overrides. @@ -88,41 +88,6 @@ export abstract class BaseDriver { */ currentUserChecker?: CurrentUserChecker; - /** - * Initializes the things driver needs before routes and middleware registration. - */ - abstract initialize(): void; - - /** - * Registers given middleware. - */ - abstract registerMiddleware(middleware: MiddlewareMetadata): void; - - /** - * Registers action in the driver. - */ - abstract registerAction(action: ActionMetadata, executeCallback: (options: Action) => any): void; - - /** - * Registers all routes in the framework. - */ - abstract registerRoutes(): void; - - /** - * Gets param from the request. - */ - abstract getParamFromRequest(actionOptions: Action, param: ParamMetadata): any; - - /** - * Defines an algorithm of how to handle error during executing controller action. - */ - abstract handleError(error: any, action: ActionMetadata, options: Action): any; - - /** - * Defines an algorithm of how to handle success result of executing controller action. - */ - abstract handleSuccess(result: any, action: ActionMetadata, options: Action): void; - // ------------------------------------------------------------------------- // Protected Methods // ------------------------------------------------------------------------- @@ -136,7 +101,7 @@ export abstract class BaseDriver { || result.pipe instanceof Function // don't transform streams ); - + // transform result if needed if (shouldTransform) { const options = action.responseClassTransformOptions || this.classToPlainTransformOptions; @@ -146,13 +111,13 @@ export abstract class BaseDriver { return result; } - protected processJsonError(error: any) { + protected processJsonError(error: any): any { if (!this.isDefaultErrorHandlingEnabled) return error; if (typeof error.toJSON === "function") return error.toJSON(); - + let processedError: any = {}; if (error instanceof Error) { const name = error.name && error.name !== "Error" ? error.name : error.constructor.name; @@ -178,7 +143,7 @@ export abstract class BaseDriver { return error; } - protected processTextError(error: any) { + protected processTextError(error: any): any { if (!this.isDefaultErrorHandlingEnabled) return error; @@ -195,17 +160,51 @@ export abstract class BaseDriver { protected merge(obj1: any, obj2: any): any { const result: any = {}; - for (let i in obj1) { + for (const i in obj1) { if ((i in obj2) && (typeof obj1[i] === "object") && (i !== null)) { result[i] = this.merge(obj1[i], obj2[i]); } else { result[i] = obj1[i]; } } - for (let i in obj2) { + for (const i in obj2) { result[i] = obj2[i]; } return result; } + /** + * Initializes the things driver needs before routes and middleware registration. + */ + abstract initialize(): void; + + /** + * Registers given middleware. + */ + abstract registerMiddleware(middleware: MiddlewareMetadata): void; + + /** + * Registers action in the driver. + */ + abstract registerAction(action: ActionMetadata, executeCallback: (options: Action) => any): void; + + /** + * Registers all routes in the framework. + */ + abstract registerRoutes(): void; + + /** + * Gets param from the request. + */ + abstract getParamFromRequest(actionOptions: Action, param: ParamMetadata): any; + + /** + * Defines an algorithm of how to handle error during executing controller action. + */ + abstract handleError(error: any, action: ActionMetadata, options: Action): any; + + /** + * Defines an algorithm of how to handle success result of executing controller action. + */ + abstract handleSuccess(result: any, action: ActionMetadata, options: Action): void; } diff --git a/src/driver/express/ExpressDriver.ts b/src/driver/express/ExpressDriver.ts index 9575a15b..5cfb3cd2 100644 --- a/src/driver/express/ExpressDriver.ts +++ b/src/driver/express/ExpressDriver.ts @@ -14,9 +14,10 @@ import {AuthorizationRequiredError} from "../../error/AuthorizationRequiredError import {NotFoundError} from "../../index"; import multer from "multer"; import bodyParser from "body-parser"; - -const cookie = require("cookie"); -const templateUrl = require("template-url"); +import cookie from "cookie"; +import templateUrl from "template-url"; +import cors from "cors"; +import express from "express"; /** * Integration with express framework. @@ -40,9 +41,8 @@ export class ExpressDriver extends BaseDriver { /** * Initializes the things driver needs before routes and middlewares registration. */ - initialize() { + initialize(): void { if (this.cors) { - const cors = require("cors"); if (this.cors === true) { this.express.use(cors()); } else { @@ -59,14 +59,14 @@ export class ExpressDriver extends BaseDriver { // if its an error handler then register it with proper signature in express if ((middleware.instance as ExpressErrorMiddlewareInterface).error) { - middlewareWrapper = (error: any, request: any, response: any, next: (err?: any) => any) => { + middlewareWrapper = (error: any, request: express.Request, response: express.Response, next: express.NextFunction): any => { (middleware.instance as ExpressErrorMiddlewareInterface).error(error, request, response, next); }; } // if its a regular middleware then register it as express middleware else if ((middleware.instance as ExpressMiddlewareInterface).use) { - middlewareWrapper = (request: any, response: any, next: (err: any) => any) => { + middlewareWrapper = (request: express.Request, response: express.Response, next: express.NextFunction): any => { try { const useResult = (middleware.instance as ExpressMiddlewareInterface).use(request, response, next); if (isPromiseLike(useResult)) { @@ -118,9 +118,9 @@ export class ExpressDriver extends BaseDriver { try { const checkResult = this.authorizationChecker(action, actionMetadata.authorizedRoles); - const handleError = (result: any) => { + const handleError = (result: any): void => { if (!result) { - let error = actionMetadata.authorizedRoles.length === 0 ? new AuthorizationRequiredError(action) : new AccessDeniedError(action); + const error = actionMetadata.authorizedRoles.length === 0 ? new AuthorizationRequiredError(action) : new AccessDeniedError(action); this.handleError(error, actionMetadata, action); } else { next(); @@ -161,7 +161,7 @@ export class ExpressDriver extends BaseDriver { // prepare route and route handler function const route = ActionMetadata.appendBaseRoute(this.routePrefix, actionMetadata.fullRoute); - const routeHandler = function routeHandler(request: any, response: any, next: Function) { + const routeHandler = function routeHandler(request: any, response: any, next: Function): any { // Express calls the "get" route automatically when we call the "head" route: // Reference: https://expressjs.com/en/4x/api.html#router.METHOD // This causes a double action execution on our side, which results in an unhandled rejection, @@ -186,7 +186,9 @@ export class ExpressDriver extends BaseDriver { /** * Registers all routes in the framework. */ - registerRoutes() { + // Why is this empty?! + registerRoutes(): void { + // Empty } /** @@ -209,7 +211,7 @@ export class ExpressDriver extends BaseDriver { case "session-param": return request.session[param.name]; - + case "session": return request.session; @@ -399,12 +401,14 @@ export class ExpressDriver extends BaseDriver { /** * Creates middlewares from the given "use"-s. */ - protected prepareMiddlewares(uses: UseMetadata[]) { + protected prepareMiddlewares(uses: UseMetadata[]): Function[] { const middlewareFunctions: Function[] = []; uses.forEach(use => { if (use.middleware.prototype && use.middleware.prototype.use) { // if this is function instance of MiddlewareInterface - middlewareFunctions.push((request: any, response: any, next: (err: any) => any) => { + middlewareFunctions.push((request: express.Request, response: express.Response, next: express.NextFunction): any => { try { + // TODO: Fix this rule + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion const useResult = (getFromContainer(use.middleware) as ExpressMiddlewareInterface).use(request, response, next); if (isPromiseLike(useResult)) { useResult.catch((error: any) => { @@ -421,6 +425,8 @@ export class ExpressDriver extends BaseDriver { } else if (use.middleware.prototype && use.middleware.prototype.error) { // if this is function instance of ErrorMiddlewareInterface middlewareFunctions.push(function (error: any, request: any, response: any, next: (err: any) => any) { + // TODO: Fix this rule + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion return (getFromContainer(use.middleware) as ExpressErrorMiddlewareInterface).error(error, request, response, next); }); @@ -434,17 +440,14 @@ export class ExpressDriver extends BaseDriver { /** * Dynamically loads express module. */ - protected loadExpress() { - if (require) { - if (!this.express) { - try { - this.express = require("express")(); - } catch (e) { - throw new Error("express package was not found installed. Try to install it: npm install express --save"); - } + // TODO: Remove me? + protected loadExpress(): void { + if (!this.express) { + try { + this.express = express(); + } catch (e) { + throw new Error("express package was not found installed. Try to install it: npm install express --save"); } - } else { - throw new Error("Cannot load express. Try to install all required dependencies."); } } } diff --git a/src/index.ts b/src/index.ts index 13ce4c3c..a91a20b2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -102,37 +102,10 @@ export function getMetadataArgsStorage(): MetadataArgsStorage { return (global as any).routingControllersMetadataArgsStorage; } -/** - * Registers all loaded actions in your express application. - */ -export function useExpressServer(expressApp: T, options?: RoutingControllersOptions): T { - const driver = new ExpressDriver(expressApp); - return createServer(driver, options); -} - -/** - * Registers all loaded actions in your express application. - */ -// TODO: Rename me to createExpressApplication -// express is an application, express.listen returns an http/https server -export function createExpressServer(options?: RoutingControllersOptions): ExpressApplication { - const driver = new ExpressDriver(); - return createServer(driver, options); -} - -/** - * Registers all loaded actions in your application using selected driver. - */ -export function createServer(driver: T, options?: RoutingControllersOptions): any { - createExecutor(driver, options); - return driver.app; -} - /** * Registers all loaded actions in your express application. */ export function createExecutor(driver: T, options: RoutingControllersOptions = {}): void { - // import all controllers and middlewares and error handlers (new way) let controllerClasses: Function[]; if (options && options.controllers && options.controllers.length) { @@ -206,11 +179,37 @@ export function createExecutor(driver: T, options: Routing .registerMiddlewares("after", middlewareClasses); // todo: register only for loaded controllers? } +/** + * Registers all loaded actions in your application using selected driver. + */ +export function createServer(driver: T, options?: RoutingControllersOptions): any { + createExecutor(driver, options); + return driver.app; +} + +/** + * Registers all loaded actions in your express application. + */ +export function useExpressServer(expressApp: T, options?: RoutingControllersOptions): T { + const driver = new ExpressDriver(expressApp); + return createServer(driver, options); +} + +/** + * Registers all loaded actions in your express application. + */ +// TODO: Rename me to createExpressApplication +// express is an application, express.listen returns an http/https server +export function createExpressServer(options?: RoutingControllersOptions): ExpressApplication { + const driver = new ExpressDriver(); + return createServer(driver, options); +} + /** * Registers custom parameter decorator used in the controller actions. */ export function createParamDecorator(options: CustomParameterDecorator) { - return function(object: Object, method: string, index: number) { + return function(object: Record, method: string, index: number): void { getMetadataArgsStorage().params.push({ type: "custom-converter", object: object, diff --git a/src/metadata-builder/MetadataArgsStorage.ts b/src/metadata-builder/MetadataArgsStorage.ts index 9a4e0384..ddba064d 100644 --- a/src/metadata-builder/MetadataArgsStorage.ts +++ b/src/metadata-builder/MetadataArgsStorage.ts @@ -142,7 +142,7 @@ export class MetadataArgsStorage { /** * Removes all saved metadata. */ - reset() { + reset(): void { this.controllers = []; this.middlewares = []; this.interceptors = []; @@ -153,4 +153,4 @@ export class MetadataArgsStorage { this.responseHandlers = []; } -} \ No newline at end of file +} diff --git a/src/metadata-builder/MetadataBuilder.ts b/src/metadata-builder/MetadataBuilder.ts index 25414fb8..081731b8 100644 --- a/src/metadata-builder/MetadataBuilder.ts +++ b/src/metadata-builder/MetadataBuilder.ts @@ -23,7 +23,7 @@ export class MetadataBuilder { /** * Builds controller metadata from a registered controller metadata args. */ - buildControllerMetadata(classes?: Function[]) { + buildControllerMetadata(classes?: Function[]): ControllerMetadata[] { return this.createControllers(classes); } @@ -105,20 +105,6 @@ export class MetadataBuilder { .map(paramArgs => new ParamMetadata(action, this.decorateDefaultParamOptions(paramArgs))); } - /** - * Decorate paramArgs with default settings - */ - private decorateDefaultParamOptions(paramArgs: ParamMetadataArgs) { - let options = this.options.defaults && this.options.defaults.paramOptions; - if (!options) - return paramArgs; - - if (paramArgs.required === undefined) - paramArgs.required = options.required || false; - - return paramArgs; - } - /** * Creates response handler metadatas for action. */ @@ -173,4 +159,17 @@ export class MetadataBuilder { .map(useArgs => new InterceptorMetadata(useArgs)); } + /** + * Decorate paramArgs with default settings + */ + private decorateDefaultParamOptions(paramArgs: ParamMetadataArgs): ParamMetadataArgs { + const options = this.options.defaults && this.options.defaults.paramOptions; + if (!options) + return paramArgs; + + if (paramArgs.required === undefined) + paramArgs.required = options.required || false; + + return paramArgs; + } } diff --git a/src/metadata/ActionMetadata.ts b/src/metadata/ActionMetadata.ts index ab96861c..f42cc69c 100644 --- a/src/metadata/ActionMetadata.ts +++ b/src/metadata/ActionMetadata.ts @@ -158,6 +158,26 @@ export class ActionMetadata { this.methodOverride = args.methodOverride; } + // ------------------------------------------------------------------------- + // Static Methods + // ------------------------------------------------------------------------- + + /** + * Appends base route to a given regexp route. + */ + // TODO: tidy me up + static appendBaseRoute(baseRoute: string, route: RegExp | string): any { + const prefix = `${baseRoute.length > 0 && !baseRoute.includes("/") ? "/" : ""}${baseRoute}`; + if (typeof route === "string") + return `${prefix}${route}`; + + if (!baseRoute || baseRoute === "") return route; + + const fullPath = `^${prefix}${route.toString().substr(1)}?$`; + + return new RegExp(fullPath, route.flags); + } + // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- @@ -166,7 +186,7 @@ export class ActionMetadata { * Builds everything action metadata needs. * Action metadata can be used only after its build. */ - build(responseHandlers: ResponseHandlerMetadata[]) { + build(responseHandlers: ResponseHandlerMetadata[]): void { const classTransformerResponseHandler = responseHandlers.find(handler => handler.type === "response-class-transform-options"); const undefinedResultHandler = responseHandlers.find(handler => handler.type === "on-undefined"); const nullResultHandler = responseHandlers.find(handler => handler.type === "on-null"); @@ -179,15 +199,15 @@ export class ActionMetadata { if (classTransformerResponseHandler) this.responseClassTransformOptions = classTransformerResponseHandler.value; - + this.undefinedResultCode = undefinedResultHandler ? undefinedResultHandler.value : this.options.defaults && this.options.defaults.undefinedResultCode; - + this.nullResultCode = nullResultHandler ? nullResultHandler.value : this.options.defaults && this.options.defaults.nullResultCode; - + if (successCodeHandler) this.successHttpCode = successCodeHandler.value; if (redirectHandler) @@ -199,8 +219,8 @@ export class ActionMetadata { this.isBodyUsed = !!this.params.find(param => param.type === "body" || param.type === "body-param"); this.isFilesUsed = !!this.params.find(param => param.type === "files"); this.isFileUsed = !!this.params.find(param => param.type === "file"); - this.isJsonTyped = (contentTypeHandler !== undefined - ? /json/.test(contentTypeHandler.value) + this.isJsonTyped = (contentTypeHandler !== undefined + ? contentTypeHandler.value.includes("json") : this.controllerMetadata.type === "json" ); this.fullRoute = this.buildFullRoute(); @@ -210,6 +230,19 @@ export class ActionMetadata { this.authorizedRoles = (this.controllerMetadata.authorizedRoles || []).concat((authorizedHandler && authorizedHandler.value) || []); } + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- + + /** + * Calls action method. + * Action method is an action defined in a user controller. + */ + callMethod(params: any[]): any { + const controllerInstance = this.controllerMetadata.instance; + return controllerInstance[this.method](...params); + } + // ------------------------------------------------------------------------- // Private Methods // ------------------------------------------------------------------------- @@ -234,7 +267,7 @@ export class ActionMetadata { /** * Builds action response headers. */ - private buildHeaders(responseHandlers: ResponseHandlerMetadata[]) { + private buildHeaders(responseHandlers: ResponseHandlerMetadata[]): { [name: string]: string } { const contentTypeHandler = responseHandlers.find(handler => handler.type === "content-type"); const locationHandler = responseHandlers.find(handler => handler.type === "location"); @@ -251,37 +284,4 @@ export class ActionMetadata { return headers; } - - // ------------------------------------------------------------------------- - // Public Methods - // ------------------------------------------------------------------------- - - /** - * Calls action method. - * Action method is an action defined in a user controller. - */ - callMethod(params: any[]) { - const controllerInstance = this.controllerMetadata.instance; - return controllerInstance[this.method].apply(controllerInstance, params); - } - - // ------------------------------------------------------------------------- - // Static Methods - // ------------------------------------------------------------------------- - - /** - * Appends base route to a given regexp route. - */ - static appendBaseRoute(baseRoute: string, route: RegExp|string) { - const prefix = `${baseRoute.length > 0 && baseRoute.indexOf("/") < 0 ? "/" : ""}${baseRoute}`; - if (typeof route === "string") - return `${prefix}${route}`; - - if (!baseRoute || baseRoute === "") return route; - - const fullPath = `^${prefix}${route.toString().substr(1)}?$`; - - return new RegExp(fullPath, route.flags); - } - -} \ No newline at end of file +} diff --git a/src/metadata/ControllerMetadata.ts b/src/metadata/ControllerMetadata.ts index d04b1fde..11cb3f74 100644 --- a/src/metadata/ControllerMetadata.ts +++ b/src/metadata/ControllerMetadata.ts @@ -83,7 +83,7 @@ export class ControllerMetadata { * Builds everything controller metadata needs. * Controller metadata should be used only after its build. */ - build(responseHandlers: ResponseHandlerMetadata[]) { + build(responseHandlers: ResponseHandlerMetadata[]): void { const authorizedHandler = responseHandlers.find(handler => handler.type === "authorized" && !handler.method); this.isAuthorizedUsed = !!authorizedHandler; this.authorizedRoles = [].concat((authorizedHandler && authorizedHandler.value) || []); diff --git a/src/typings/template-url.d.ts b/src/typings/template-url.d.ts new file mode 100644 index 00000000..3d727272 --- /dev/null +++ b/src/typings/template-url.d.ts @@ -0,0 +1 @@ +declare module "template-url"; diff --git a/src/util/container.ts b/src/util/container.ts index cc18963d..5b18b7fb 100644 --- a/src/util/container.ts +++ b/src/util/container.ts @@ -39,7 +39,7 @@ let userContainerOptions: UseContainerOptions; /** * Sets container to be used by this library. */ -export function useContainer(iocContainer: { get(someClass: any): any }, options?: UseContainerOptions) { +export function useContainer(iocContainer: { get(someClass: any): any }, options?: UseContainerOptions): void { userContainer = iocContainer; userContainerOptions = options; } diff --git a/src/util/importClassesFromDirectories.ts b/src/util/importClassesFromDirectories.ts index 8e08786c..17d55845 100644 --- a/src/util/importClassesFromDirectories.ts +++ b/src/util/importClassesFromDirectories.ts @@ -5,7 +5,7 @@ import * as path from "path"; */ export function importClassesFromDirectories(directories: string[], formats = [".js", ".ts"]): Function[] { - const loadFileClasses = function (exported: any, allLoaded: Function[]) { + const loadFileClasses = function (exported: any, allLoaded: Function[]): Function[] { if (exported instanceof Function) { allLoaded.push(exported); } else if (exported instanceof Array) { @@ -24,11 +24,11 @@ export function importClassesFromDirectories(directories: string[], formats = [" const dirs = allFiles .filter(file => { const dtsExtension = file.substring(file.length - 5, file.length); - return formats.indexOf(path.extname(file)) !== -1 && dtsExtension !== ".d.ts"; + return formats.includes(path.extname(file)) && dtsExtension !== ".d.ts"; }) .map(file => { return require(file); }); return loadFileClasses(dirs, []); -} \ No newline at end of file +} diff --git a/test/fakes/global-options/FakeService.ts b/test/fakes/global-options/FakeService.ts index d0535356..33140da5 100644 --- a/test/fakes/global-options/FakeService.ts +++ b/test/fakes/global-options/FakeService.ts @@ -5,27 +5,27 @@ export class FakeService { questionErrorMiddlewareCalled = false; postMiddlewareCalled = false; - fileMiddleware() { + fileMiddleware(): void { this.fileMiddlewareCalled = true; } - videoMiddleware() { + videoMiddleware(): void { this.videoMiddlewareCalled = true; } - questionMiddleware() { + questionMiddleware(): void { this.questionMiddlewareCalled = true; } - questionErrorMiddleware() { + questionErrorMiddleware(): void { this.questionErrorMiddlewareCalled = true; } - postMiddleware() { + postMiddleware(): void { this.postMiddlewareCalled = true; } - reset() { + reset(): void { this.fileMiddlewareCalled = false; this.videoMiddlewareCalled = false; this.questionMiddlewareCalled = false; diff --git a/test/fakes/global-options/first-controllers/post/PostController.ts b/test/fakes/global-options/first-controllers/post/PostController.ts index 1b6dbc19..a80bf993 100644 --- a/test/fakes/global-options/first-controllers/post/PostController.ts +++ b/test/fakes/global-options/first-controllers/post/PostController.ts @@ -3,9 +3,8 @@ import {Get} from "../../../../../src/decorator/Get"; @JsonController() export class PostController { - @Get("/posts") - getAll() { + getAll(): any { return [{ id: 1, title: "#1" @@ -14,5 +13,4 @@ export class PostController { title: "#2" }]; } - -} \ No newline at end of file +} diff --git a/test/fakes/global-options/first-controllers/question/AnswerController.ts b/test/fakes/global-options/first-controllers/question/AnswerController.ts index b620301c..a7438d2f 100644 --- a/test/fakes/global-options/first-controllers/question/AnswerController.ts +++ b/test/fakes/global-options/first-controllers/question/AnswerController.ts @@ -3,9 +3,8 @@ import {Get} from "../../../../../src/decorator/Get"; @JsonController() export class AnswerController { - @Get("/answers") - getAll() { + getAll(): any { return [{ id: 1, title: "#1" @@ -14,5 +13,4 @@ export class AnswerController { title: "#2" }]; } - -} \ No newline at end of file +} diff --git a/test/fakes/global-options/first-controllers/question/QuestionController.ts b/test/fakes/global-options/first-controllers/question/QuestionController.ts index f893a31c..a4a4f384 100644 --- a/test/fakes/global-options/first-controllers/question/QuestionController.ts +++ b/test/fakes/global-options/first-controllers/question/QuestionController.ts @@ -3,9 +3,8 @@ import {Get} from "../../../../../src/decorator/Get"; @JsonController() export class QuestionController { - @Get("/questions") - getAll() { + getAll(): any { return [{ id: 1, title: "#1" @@ -14,5 +13,4 @@ export class QuestionController { title: "#2" }]; } - -} \ No newline at end of file +} diff --git a/test/fakes/global-options/second-controllers/PhotoController.ts b/test/fakes/global-options/second-controllers/PhotoController.ts index afd0af03..cf92d8a6 100644 --- a/test/fakes/global-options/second-controllers/PhotoController.ts +++ b/test/fakes/global-options/second-controllers/PhotoController.ts @@ -3,10 +3,8 @@ import {Get} from "../../../../src/decorator/Get"; @Controller() export class PhotoController { - @Get("/photos") - getAll() { + getAll(): string { return "Hello photos"; } - -} \ No newline at end of file +} diff --git a/test/fakes/global-options/second-controllers/VideoController.ts b/test/fakes/global-options/second-controllers/VideoController.ts index b1fcfc56..2ff8929b 100644 --- a/test/fakes/global-options/second-controllers/VideoController.ts +++ b/test/fakes/global-options/second-controllers/VideoController.ts @@ -3,10 +3,8 @@ import {Get} from "../../../../src/decorator/Get"; @Controller() export class VideoController { - @Get("/videos") - getAll() { + getAll(): string { return "Hello videos"; } - -} \ No newline at end of file +} diff --git a/test/functional/action-params.spec.ts b/test/functional/action-params.spec.ts index 2e221a65..7ce66463 100644 --- a/test/functional/action-params.spec.ts +++ b/test/functional/action-params.spec.ts @@ -36,13 +36,13 @@ let expressServer: HttpServer; let paramUserId: number | undefined, paramFirstId: number | undefined, paramSecondId: number | undefined; let sessionTestElement: string | undefined; let queryParamSortBy: string | undefined, queryParamCount: string | undefined, queryParamLimit: number | undefined, - queryParamShowAll: boolean | undefined, queryParamFilter: Object | undefined; + queryParamShowAll: boolean | undefined, queryParamFilter: Record | undefined; let queryParams1: { [key: string]: any } | undefined, queryParams2: { [key: string]: any } | undefined, queryParams3: { [key: string]: any } | undefined; let headerParamToken: string | undefined, headerParamCount: number | undefined, headerParamLimit: number | undefined, - headerParamShowAll: boolean | undefined, headerParamFilter: Object | undefined; + headerParamShowAll: boolean | undefined, headerParamFilter: Record | undefined; let cookieParamToken: string | undefined, cookieParamCount: number | undefined, cookieParamLimit: number | undefined, - cookieParamShowAll: boolean | undefined, cookieParamFilter: Object | undefined; + cookieParamShowAll: boolean | undefined, cookieParamFilter: Record | undefined; let body: string | undefined; let bodyParamName: string | undefined, bodyParamAge: number | undefined, bodyParamIsActive: boolean | undefined; let expressRequest: express.Request | undefined, expressResponse: express.Response | undefined; @@ -325,26 +325,26 @@ beforeAll((done) => { @JsonController() class SecondUserActionParamsController { @Post("/posts") - postPost(@Body() question: any) { + postPost(@Body() question: any): any { body = question; return body; } @Post("/posts-with-required") - postRequiredPost(@Body({required: true}) post: string) { + postRequiredPost(@Body({required: true}) post: string): any { body = post; return body; } @Get("/posts-after") - getPostsAfter(@QueryParam("from", {required: true}) from: Date): any { + getPostsAfter(@QueryParam("from", {required: true}) from: Date): string { return from.toISOString(); } @Post("/users") postUser(@BodyParam("name") name: string, @BodyParam("age") age: number, - @BodyParam("isActive") isActive: boolean): any { + @BodyParam("isActive") isActive: boolean): null { bodyParamName = name; bodyParamAge = age; bodyParamIsActive = isActive; @@ -354,7 +354,7 @@ beforeAll((done) => { @Post("/users-with-required") postUserWithRequired(@BodyParam("name", {required: true}) name: string, @BodyParam("age", {required: true}) age: number, - @BodyParam("isActive", {required: true}) isActive: boolean): any { + @BodyParam("isActive", {required: true}) isActive: boolean): null { bodyParamName = name; bodyParamAge = age; bodyParamIsActive = isActive; @@ -833,7 +833,7 @@ it("@Body using application/x-www-form-urlencoded should handle url encoded form it("@UploadedFile using multipart/form-data should provide uploaded file with the given name", () => { expect.assertions(3); - let form = new FormData(); + const form = new FormData(); form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); return axios.post("/file", form, { headers: form.getHeaders() @@ -846,7 +846,7 @@ it("@UploadedFile using multipart/form-data should provide uploaded file with th it("@UploadedFile with @Body should return both the file and the body", () => { expect.assertions(3); - let form = new FormData(); + const form = new FormData(); form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); form.append("anotherField", "hello"); form.append("andAnother", "world"); @@ -861,7 +861,7 @@ it("@UploadedFile with @Body should return both the file and the body", () => { it("@UploadedFile with @BodyParam should return both the file and the body param", () => { expect.assertions(3); - let form = new FormData(); + const form = new FormData(); form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); form.append("testParam", "testParamOne"); return axios.post("/file-with-body-param", form, { @@ -875,7 +875,7 @@ it("@UploadedFile with @BodyParam should return both the file and the body param it("@UploadedFile with passed uploading options (limit) should throw an error", () => { expect.assertions(1); - let form = new FormData(); + const form = new FormData(); form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); return axios.post("/file-with-limit", form, { headers: form.getHeaders() @@ -886,7 +886,7 @@ it("@UploadedFile with passed uploading options (limit) should throw an error", it("@UploadedFile when required is used files must be provided", () => { expect.assertions(4); - let form = new FormData(); + const form = new FormData(); form.append("myFile", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); return Promise.all([ axios.post("/file-with-required", form, { @@ -906,7 +906,7 @@ it("@UploadedFile when required is used files must be provided", () => { it("@UploadedFiles should provide uploaded files with the given name", () => { expect.assertions(3); - let form = new FormData(); + const form = new FormData(); form.append("photos", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); form.append("photos", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-two.txt"))); return axios.post("/photos", form, { @@ -920,7 +920,7 @@ it("@UploadedFiles should provide uploaded files with the given name", () => { it("@UploadedFiles with passed uploading options (limit) should throw an error", () => { expect.assertions(1); - let form = new FormData(); + const form = new FormData(); form.append("photos", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt"))); form.append("photos", fs.createReadStream(path.resolve(__dirname, "../resources/sample-text-file-two.txt"))); return axios.post("/photos-with-limit", form, { @@ -932,7 +932,7 @@ it("@UploadedFiles with passed uploading options (limit) should throw an error", it("@UploadedFiles when required is used files must be provided", () => { expect.assertions(1); - let form = new FormData(); + const form = new FormData(); return axios.post("/photos-with-required", undefined, { headers: form.getHeaders() }).catch((error: AxiosError) => { diff --git a/test/functional/auth-decorator.spec.ts b/test/functional/auth-decorator.spec.ts index c087c633..189b55b9 100644 --- a/test/functional/auth-decorator.spec.ts +++ b/test/functional/auth-decorator.spec.ts @@ -10,9 +10,9 @@ import {AxiosError, AxiosResponse} from "axios"; import HttpStatusCodes from "http-status-codes"; import {axios} from "../utilities/axios"; -const sleep = (time: number) => new Promise(resolve => setTimeout(resolve, time)); +const sleep = (time: number): Promise => new Promise(resolve => setTimeout(resolve, time)); -describe("Controller responds with value when Authorization succeeds (async)", function () { +describe("Controller responds with value when Authorization succeeds (async)", () => { let expressServer: HttpServer; beforeEach((done: DoneCallback) => { @@ -22,19 +22,19 @@ describe("Controller responds with value when Authorization succeeds (async)", f class AuthController { @Authorized() @Get("/auth1") - auth1() { + auth1(): any { return {test: "auth1"}; } @Authorized(["role1"]) @Get("/auth2") - auth2() { + auth2(): any { return {test: "auth2"}; } @Authorized() @Get("/auth3") - async auth3() { + async auth3(): Promise { await sleep(10); return {test: "auth3"}; } @@ -88,26 +88,26 @@ describe("Controller responds with value when Authorization succeeds (sync)", () class AuthController { @Authorized() @Get("/auth1") - auth1() { + auth1(): any { return {test: "auth1"}; } @Authorized(["role1"]) @Get("/auth2") - auth2() { + auth2(): any { return {test: "auth2"}; } @Authorized() @Get("/auth3") - async auth3() { + async auth3(): Promise { await sleep(10); return {test: "auth3"}; } } expressServer = createExpressServer({ - authorizationChecker: async (action: Action, roles?: string[]) => { + authorizationChecker: (action: Action, roles?: string[]) => { return true; } }).listen(3001, done); @@ -153,19 +153,19 @@ describe("Authorized Decorators Http Status Code", () => { class AuthController { @Authorized() @Get("/auth1") - auth1() { + auth1(): any { return {test: "auth1"}; } @Authorized(["role1"]) @Get("/auth2") - auth2() { + auth2(): any { return {test: "auth2"}; } } expressServer = createExpressServer({ - authorizationChecker: async (action: Action, roles?: string[]) => { + authorizationChecker: (action: Action, roles?: string[]) => { return false; } }).listen(3001, done); @@ -200,13 +200,13 @@ describe("Authorization checker allows to throw (async)", () => { class AuthController { @Authorized() @Get("/auth1") - auth1() { + auth1(): any { return {test: "auth1"}; } } expressServer = createExpressServer({ - authorizationChecker: async (action: Action, roles?: string[]) => { + authorizationChecker: (action: Action, roles?: string[]) => { throw new NotAcceptableError("Custom Error"); } }).listen(3001, done); @@ -236,13 +236,13 @@ describe("Authorization checker allows to throw (sync)", () => { class AuthController { @Authorized() @Get("/auth1") - auth1() { + auth1(): any { return {test: "auth1"}; } } expressServer = createExpressServer({ - authorizationChecker: async (action: Action, roles?: string[]) => { + authorizationChecker: (action: Action, roles?: string[]) => { throw new NotAcceptableError("Custom Error"); } }).listen(3001, done); diff --git a/test/functional/container.spec.ts b/test/functional/container.spec.ts index 36c02622..6eeb06d0 100644 --- a/test/functional/container.spec.ts +++ b/test/functional/container.spec.ts @@ -106,7 +106,7 @@ describe("using custom container should be possible", () => { const fakeContainer = { services: [] as any, - get(service: any) { + get(service: any): any { if (!this.services[service.name]) { this.services[service.name] = new service(); } diff --git a/test/functional/controller-base-routes.spec.ts b/test/functional/controller-base-routes.spec.ts index a2653d34..440f499f 100644 --- a/test/functional/controller-base-routes.spec.ts +++ b/test/functional/controller-base-routes.spec.ts @@ -17,22 +17,22 @@ describe("controller > base routes functionality", () => { @Controller("/posts") class PostController { @Get("/") - getAll() { + getAll(): string { return "All posts"; } @Get("/:id(\\d+)") - getUserById() { + getUserById(): string { return "One post"; } @Get(/\/categories\/(\d+)/) - getCategoryById() { + getCategoryById(): string { return "One post category"; } @Get("/:postId(\\d+)/users/:userId(\\d+)") - getPostById() { + getPostById(): string { return "One user"; } } diff --git a/test/functional/controller-methods.spec.ts b/test/functional/controller-methods.spec.ts index c1cfea11..266f0d2a 100644 --- a/test/functional/controller-methods.spec.ts +++ b/test/functional/controller-methods.spec.ts @@ -26,62 +26,62 @@ describe("controller methods", () => { @Controller() class UserController { @Get("/users") - getAll() { + getAll(): string { return "All users"; } @Post("/users") - post() { + post(): string { return "Posting user"; } @Put("/users") - put() { + put(): string { return "Putting user"; } @Patch("/users") - patch() { + patch(): string { return "Patching user"; } @Delete("/users") - delete() { + delete(): string { return "Removing user"; } @Head("/users") - head() { + head(): string { return "Removing user"; } @Method("post", "/categories") - postCategories() { + postCategories(): string { return "Posting categories"; } @Method("delete", "/categories") - getCategories() { + getCategories(): string { return "Get categories"; } @Get("/users/:id") - getUserById() { + getUserById(): string { return "One user"; } @Get(/\/categories\/[\d+]/) - getCategoryById() { + getCategoryById(): string { return "One category"; } @Get("/posts/:id(\\d+)") - getPostById() { + getPostById(): string { return "One post"; } @Get("/posts-from-db") - getPostFromDb() { + getPostFromDb(): Promise { return new Promise((ok, fail) => { setTimeout(() => { ok("resolved after half second"); @@ -90,7 +90,7 @@ describe("controller methods", () => { } @Get("/posts-from-failed-db") - getPostFromFailedDb() { + getPostFromFailedDb(): Promise { return new Promise((ok, fail) => { setTimeout(() => { fail("cannot connect to a database"); diff --git a/test/functional/defaults.spec.ts b/test/functional/defaults.spec.ts index d85c38dc..f03a0fcd 100644 --- a/test/functional/defaults.spec.ts +++ b/test/functional/defaults.spec.ts @@ -21,35 +21,35 @@ describe("defaults", () => { @Controller() class ExpressController { @Get("/voidfunc") - voidFunc() { + voidFunc(): void { // Empty } @Get("/promisevoidfunc") - promiseVoidFunc() { + promiseVoidFunc(): Promise { return Promise.resolve(); } @Get("/paramfunc") - paramFunc(@QueryParam("x") x: number) { + paramFunc(@QueryParam("x") x: number): any { return { foo: "bar" }; } @Get("/nullfunc") - nullFunc(): string { + nullFunc(): null { return null; } @Get("/overridefunc") @OnUndefined(HttpStatusCodes.NOT_ACCEPTABLE) - overrideFunc() { + overrideFunc(): void { // Empty } @Get("/overrideparamfunc") - overrideParamFunc(@QueryParam("x", {required: false}) x: number) { + overrideParamFunc(@QueryParam("x", {required: false}) x: number): any { return { foo: "bar" }; diff --git a/test/functional/express-custom-error-handling.spec.ts b/test/functional/express-custom-error-handling.spec.ts index f387f9db..cd85771a 100644 --- a/test/functional/express-custom-error-handling.spec.ts +++ b/test/functional/express-custom-error-handling.spec.ts @@ -34,7 +34,7 @@ describe("custom express error handling", () => { @JsonController() class ExpressErrorHandlerController { @Get("/blogs") - blogs() { + blogs(): any { return { id: 1, title: "About me" @@ -42,7 +42,7 @@ describe("custom express error handling", () => { } @Get("/videos") - videos() { + videos(): never { throw new NotFoundError("Videos were not found."); } } diff --git a/test/functional/express-error-handling.spec.ts b/test/functional/express-error-handling.spec.ts index 415ff087..053182e1 100644 --- a/test/functional/express-error-handling.spec.ts +++ b/test/functional/express-error-handling.spec.ts @@ -62,7 +62,7 @@ describe("express error handling", () => { this.secretData = privateMsg || "secret"; } - toJSON() { + toJSON(): any { return { status: this.httpCode, publicData: `${this.publicData} (${this.httpCode})` @@ -73,7 +73,7 @@ describe("express error handling", () => { @JsonController() class ExpressErrorHandlerController { @Get("/blogs") - blogs() { + blogs(): any { return { id: 1, title: "About me" @@ -81,34 +81,34 @@ describe("express error handling", () => { } @Get("/posts") - posts() { + posts(): never { throw new Error("System error, cannot retrieve posts"); } @Get("/videos") - videos() { + videos(): never { throw new NotFoundError("Videos were not found."); } @Get("/questions") @UseAfter(SpecificErrorHandler) - questions() { + questions(): never { throw new Error("Something is wrong... Cannot load questions"); } @Get("/files") @UseAfter(SoftErrorHandler) - files() { + files(): never { throw new Error("Something is wrong... Cannot load files"); } @Get("/photos") - photos() { + photos(): string { return "1234"; } @Get("/stories") - stories() { + stories(): never { throw new ToJsonError(503, "sorry, try it again later", "impatient user"); } } diff --git a/test/functional/express-global-before-error-handling.spec.ts b/test/functional/express-global-before-error-handling.spec.ts index e951b040..d136adc0 100644 --- a/test/functional/express-global-before-error-handling.spec.ts +++ b/test/functional/express-global-before-error-handling.spec.ts @@ -37,7 +37,7 @@ describe("custom express global before middleware error handling", () => { @Middleware({type: "after"}) class CustomErrorHandler implements ExpressErrorMiddlewareInterface { - error(error: any, req: any, res: any, next: any) { + error(error: any, req: any, res: any, next: any): void { errorHandlerCalled = true; errorHandlerName = error.name; res.status(error.httpCode || 500).send(error.message); @@ -46,9 +46,8 @@ describe("custom express global before middleware error handling", () => { @JsonController() class ExpressErrorHandlerController { - @Get("/answers") - answers() { + answers(): any { return { id: 1, title: "My answer" diff --git a/test/functional/express-middlewares.spec.ts b/test/functional/express-middlewares.spec.ts index b41f4736..6602b271 100644 --- a/test/functional/express-middlewares.spec.ts +++ b/test/functional/express-middlewares.spec.ts @@ -74,14 +74,14 @@ describe("express middlewares", () => { @Controller() class ExpressMiddlewareController { @Get("/blogs") - blogs() { + blogs(): string { useGlobalCallOrder = "setFromController"; return "1234"; } @Get("/questions") @UseBefore(TestLoggerMiddleware) - questions() { + questions(): string { return "1234"; } @@ -91,7 +91,7 @@ describe("express middlewares", () => { useCallOrder = "setFromUseBefore"; next(); }) - users() { + users(): string { useCallOrder = "setFromController"; return "1234"; } @@ -102,7 +102,7 @@ describe("express middlewares", () => { useCallOrder = "setFromUseAfter"; next(); }) - photos() { + photos(): string { useCallOrder = "setFromController"; return "1234"; } @@ -118,14 +118,14 @@ describe("express middlewares", () => { useCallOrder = "setFromUseAfter"; next(); }) - posts() { + posts(): string { useCallOrder = "setFromController"; return "1234"; } @Get("/customMiddlewareWichThrows") @UseBefore(TestCustomMiddlewareWhichThrows) - customMiddlewareWichThrows() { + customMiddlewareWichThrows(): string { return "1234"; } } diff --git a/test/functional/global-options.spec.ts b/test/functional/global-options.spec.ts index d7d950aa..ec2b60cd 100644 --- a/test/functional/global-options.spec.ts +++ b/test/functional/global-options.spec.ts @@ -24,13 +24,13 @@ getMetadataArgsStorage().reset(); @JsonController() class TestUserController { @Post("/users") - postUsers(@Body() user: User) { + postUsers(@Body() user: User): string { initializedUser = user; return ""; } @Post(new RegExp("/(prefix|regex)/users")) - postUsersWithRegex(@Body() user: User) { + postUsersWithRegex(@Body() user: User): string { initializedUser = user; return ""; } diff --git a/test/functional/json-controller-methods.spec.ts b/test/functional/json-controller-methods.spec.ts index 45d63d1a..6cfe660b 100644 --- a/test/functional/json-controller-methods.spec.ts +++ b/test/functional/json-controller-methods.spec.ts @@ -23,7 +23,7 @@ describe("json-controller methods", () => { @JsonController() class JsonUserController { @Get("/users") - getAll() { + getAll(): any { return [{ id: 1, name: "Umed" @@ -34,56 +34,56 @@ describe("json-controller methods", () => { } @Post("/users") - post() { + post(): any { return { status: "saved" }; } @Put("/users") - put() { + put(): any { return { status: "updated" }; } @Patch("/users") - patch() { + patch(): any { return { status: "patched" }; } @Delete("/users") - delete() { + delete(): any { return { status: "removed" }; } @Head("/users") - head() { + head(): any { return { thisWillNot: "beSent" }; } @Method("post", "/categories") - postCategories() { + postCategories(): any { return { status: "posted" }; } @Method("delete", "/categories") - getCategories() { + getCategories(): any { return { status: "removed" }; } @Get("/users/:id") - getUserById() { + getUserById(): any { return { id: 1, name: "Umed" @@ -91,7 +91,7 @@ describe("json-controller methods", () => { } @Get(/\/categories\/[\d+]/) - getCategoryById() { + getCategoryById(): any { return { id: 1, name: "People" @@ -99,7 +99,7 @@ describe("json-controller methods", () => { } @Get("/posts/:id(\\d+)") - getPostById() { + getPostById(): any { return { id: 1, title: "About People" @@ -107,7 +107,7 @@ describe("json-controller methods", () => { } @Get("/posts-from-db") - getPostFromDb() { + getPostFromDb(): Promise { return new Promise((ok, fail) => { setTimeout(() => { ok({ @@ -119,7 +119,7 @@ describe("json-controller methods", () => { } @Get("/posts-from-failed-db") - getPostFromFailedDb() { + getPostFromFailedDb(): Promise { return new Promise((ok, fail) => { setTimeout(() => { fail({ diff --git a/test/functional/middlewares-order.spec.ts b/test/functional/middlewares-order.spec.ts index f19c9704..7224d830 100644 --- a/test/functional/middlewares-order.spec.ts +++ b/test/functional/middlewares-order.spec.ts @@ -49,7 +49,7 @@ describe("loaded direct from array", () => { @Controller() class ExpressMiddlewareController { @Get("/test") - test() { + test(): string { return "OK"; } } @@ -111,7 +111,7 @@ describe("specified by priority option", () => { @Controller() class ExpressMiddlewareController { @Get("/test") - test() { + test(): string { return "OK"; } } diff --git a/test/functional/other-controller-decorators.spec.ts b/test/functional/other-controller-decorators.spec.ts index 008eb318..fcf0e74d 100644 --- a/test/functional/other-controller-decorators.spec.ts +++ b/test/functional/other-controller-decorators.spec.ts @@ -36,19 +36,19 @@ describe("other controller decorators", () => { class OtherDectoratorsController { @Post("/users") @HttpCode(201) - getUsers() { + getUsers(): string { return "User has been created"; } @Get("/admin") @HttpCode(403) - getAdmin() { + getAdmin(): string { return "Access is denied"; } @Get("/posts/:id") @OnNull(404) - getPost(@Param("id") id: number) { + getPost(@Param("id") id: number): Promise { return new Promise((ok, fail) => { if (id === 1) { ok("Post"); @@ -67,7 +67,7 @@ describe("other controller decorators", () => { @Get("/photos/:id") @OnUndefined(201) - getPhoto(@Param("id") id: number) { + getPhoto(@Param("id") id: number): Promise { if (id === 4) { return undefined; } @@ -90,32 +90,32 @@ describe("other controller decorators", () => { @Get("/homepage") @ContentType("text/html; charset=utf-8") - getHomepage() { + getHomepage(): string { return "Hello world"; } @Get("/textpage") @ContentType("text/plain; charset=utf-8") - getTextpage() { + getTextpage(): string { return "Hello text"; } @Get("/userdash") @Header("authorization", "Barer abcdefg") @Header("development-mode", "enabled") - getUserdash() { + getUserdash(): string { return "Hello, User"; } @Get("/github") @Location("http://github.com") - getToGithub() { + getToGithub(): string { return "Hello, github"; } @Get("/github-redirect") @Redirect("http://github.com") - goToGithub() { // todo: need test for this one + goToGithub(): string { // todo: need test for this one return "Hello, github"; } } @@ -124,7 +124,7 @@ describe("other controller decorators", () => { class JsonOtherDectoratorsController { @Get("/questions/:id") @OnUndefined(QuestionNotFoundError) - getPosts(@Param("id") id: number) { + getPosts(@Param("id") id: number): Promise { return new Promise((ok, fail) => { if (id === 1) { ok("Question"); diff --git a/test/functional/redirect-decorator.spec.ts b/test/functional/redirect-decorator.spec.ts index 7671bec2..592f5363 100644 --- a/test/functional/redirect-decorator.spec.ts +++ b/test/functional/redirect-decorator.spec.ts @@ -19,7 +19,7 @@ describe("dynamic redirect", function () { @JsonController("/users") class TestController { @Get("/:id") - async getOne(@Param("id") id: string) { + getOne(@Param("id") id: string): any { return { login: id }; @@ -30,18 +30,19 @@ describe("dynamic redirect", function () { class RedirectController { @Get("/template") @Redirect("/users/:owner") - template() { + template(): any { return {owner: "pleerock", repo: "routing-controllers"}; } @Get("/original") @Redirect("/users/pleerock") - original() { + original(): void { + // Empty } @Get("/override") @Redirect("https://api.github.com") - override() { + override(): string { return "/users/pleerock"; } } diff --git a/test/functional/render-decorator.spec.ts b/test/functional/render-decorator.spec.ts index a0c6ae5d..00b7fe12 100644 --- a/test/functional/render-decorator.spec.ts +++ b/test/functional/render-decorator.spec.ts @@ -23,7 +23,7 @@ describe("template rendering", () => { class RenderController { @Get("/index") @Render("render-test-spec.html") - index() { + index(): any { return { name: "Routing-controllers" }; @@ -31,7 +31,7 @@ describe("template rendering", () => { @Get("/locals") @Render("render-test-locals-spec.html") - locals(@Res() res: any) { + locals(@Res() res: any): any { res.locals.myVariable = "my-variable"; return { @@ -41,7 +41,7 @@ describe("template rendering", () => { } const resourcePath: string = path.resolve(__dirname, "../resources"); - let expressApplication: ExpressApplication = createExpressServer(); + const expressApplication: ExpressApplication = createExpressServer(); expressApplication.engine("html", mustacheExpress()); expressApplication.set("view engine", "html"); expressApplication.set("views", resourcePath); diff --git a/test/functional/special-result-send.spec.ts b/test/functional/special-result-send.spec.ts index 5d760216..b2758ee2 100644 --- a/test/functional/special-result-send.spec.ts +++ b/test/functional/special-result-send.spec.ts @@ -10,6 +10,7 @@ import {Server as HttpServer} from "http"; import HttpStatusCodes from "http-status-codes"; import DoneCallback = jest.DoneCallback; import {axios} from "../utilities/axios"; +import ReadableStream = NodeJS.ReadableStream; describe("special result value treatment", () => { let expressServer: HttpServer; @@ -22,19 +23,19 @@ describe("special result value treatment", () => { class HandledController { @Get("/stream") @ContentType("text/plain") - getStream() { + getStream(): ReadableStream { return createReadStream(path.resolve(__dirname, "../resources/sample-text-file-one.txt")); } @Get("/buffer") @ContentType("application/octet-stream") - getBuffer() { + getBuffer(): Buffer { return Buffer.from(rawData); } @Get("/array") @ContentType("application/octet-stream") - getUIntArray() { + getUIntArray(): Uint8Array { return new Uint8Array(rawData); } } @@ -47,11 +48,11 @@ describe("special result value treatment", () => { it("should pipe stream to response", () => { expect.assertions(3); return axios.get("/stream") - .then(async (response: AxiosResponse) => { - expect(response.status).toEqual(HttpStatusCodes.OK); - expect(response.headers["content-type"]).toEqual("text/plain; charset=utf-8"); - expect(response.data).toEqual("Hello World!"); - }); + .then((response: AxiosResponse) => { + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.headers["content-type"]).toEqual("text/plain; charset=utf-8"); + expect(response.data).toEqual("Hello World!"); + }); }); it("should send raw binary data from Buffer", () => { From 5cc97a3d39732f50bbeefa1a67c845fe8abd79cb Mon Sep 17 00:00:00 2001 From: Rob Muchall Date: Fri, 3 Apr 2020 11:59:41 +0100 Subject: [PATCH 3/4] Fixed package script --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index c7004613..ba54de87 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ + "outDir": "./build/compiled", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ From b7baf869bdd6a435598845497e7e89a0c6519daa Mon Sep 17 00:00:00 2001 From: Rob Muchall Date: Thu, 23 Apr 2020 17:50:02 +0100 Subject: [PATCH 4/4] Updated test script to use runInBand --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6cd6d141..b3ed2137 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,6 @@ "package": "npm run build && npm run copy && npm run public && rimraf build/compiled", "pretest": "npm run lint", "public": "json -I -f build/package/package.json -e 'this.private=false'", - "test": "rimraf coverage && jest --coverage" + "test": "rimraf coverage && jest --coverage --runInBand" } }