├── .gitignore ├── README.md ├── env └── .env.example ├── package-lock.json ├── package.json ├── src ├── AppInitilizer.ts ├── configuration │ ├── Config.ts │ ├── DIContainer.ts │ └── Logger.ts ├── core │ ├── component │ │ ├── event │ │ │ ├── EventContainerModule.ts │ │ │ ├── application │ │ │ │ ├── data │ │ │ │ │ ├── input │ │ │ │ │ │ └── EventCreationInput.ts │ │ │ │ │ └── output │ │ │ │ │ │ └── EventResponse.ts │ │ │ │ └── services │ │ │ │ │ ├── EventCreationService.ts │ │ │ │ │ ├── EventQueryService.ts │ │ │ │ │ ├── EventReminderService.ts │ │ │ │ │ └── converters │ │ │ │ │ └── EventConverter.ts │ │ │ ├── domain │ │ │ │ └── data │ │ │ │ │ ├── Event.ts │ │ │ │ │ └── EventType.ts │ │ │ └── port │ │ │ │ └── EventRepository.ts │ │ └── user │ │ │ ├── UserContainerModule.ts │ │ │ ├── application │ │ │ ├── data │ │ │ │ ├── input │ │ │ │ │ ├── UserNotificationTokenInput.ts │ │ │ │ │ └── UserRegistrationInput.ts │ │ │ │ └── output │ │ │ │ │ └── UserResponse.ts │ │ │ └── services │ │ │ │ ├── UserNotificationService.ts │ │ │ │ ├── UserRegistrationService.ts │ │ │ │ └── converters │ │ │ │ └── UserConverter.ts │ │ │ ├── domain │ │ │ └── data │ │ │ │ └── User.ts │ │ │ └── port │ │ │ └── UserRepository.ts │ ├── port │ │ └── notification │ │ │ ├── NotificationService.ts │ │ │ └── data │ │ │ └── NotificationMessage.ts │ └── sharedKernel │ │ ├── annotations │ │ └── transformers.ts │ │ ├── exceptions │ │ ├── BadRequest.ts │ │ ├── DuplicationError.ts │ │ ├── Forbidden.ts │ │ ├── NotFound.ts │ │ └── Unauthorized.ts │ │ └── interfaces │ │ └── Converter.ts ├── migrations │ ├── 1566649710185-CreateUser.ts │ └── 1566649715982-CreateEvent.ts ├── primaryAdapters │ └── rest │ │ ├── RestContainerModule.ts │ │ ├── common │ │ ├── HttpErrorHandlers.ts │ │ └── data │ │ │ └── ApiError.ts │ │ ├── event │ │ └── EventController.ts │ │ └── user │ │ └── UserController.ts ├── secondaryAdapters │ ├── notification │ │ ├── FCMNotificationAdapter.ts │ │ └── NotificationContainerModule.ts │ └── postgres │ │ ├── Connection.ts │ │ ├── PostgresContainerModule.ts │ │ ├── common │ │ └── BaseTypeORMRepository.ts │ │ ├── event │ │ ├── converters │ │ │ └── EventEntityConverter.ts │ │ ├── data │ │ │ └── EventEntity.ts │ │ └── repository │ │ │ └── EventRepositoryAdapter.ts │ │ └── user │ │ ├── converters │ │ └── UserEntityConverter.ts │ │ ├── data │ │ └── UserEntity.ts │ │ └── repository │ │ └── UserRepositoryAdapter.ts └── server.ts ├── test ├── acceptance │ ├── base.ts │ ├── event │ │ └── eventTest.spec.ts │ └── user │ │ └── userTest.spec.ts ├── common │ ├── constructionObjects.ts │ └── mocks.ts └── unit │ ├── base.ts │ ├── event │ ├── eventCreationServiceTest.spec.ts │ └── eventReminderServiceTest.spec.ts │ └── user │ └── userRegistrationServiceTest.spec.ts ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # next.js build output 63 | .next 64 | 65 | .idea/ 66 | .terraform/ 67 | dist/ 68 | .env.* 69 | !.env.example 70 | !.env.ci 71 | 72 | *.tfstate 73 | *.tfstate.* 74 | !*.tfstate.example 75 | 76 | *.tfvars 77 | 78 | *.pem 79 | *.pem.pub 80 | 81 | .nyc_output 82 | coverage 83 | *.lcov 84 | .DS_Store 85 | dump.rdb 86 | /postgresdb/ 87 | /concat_sql.py 88 | /out.sql 89 | 90 | firebase-magic-auth-file.json 91 | /.vscode/ 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hexagonal architecture demo 2 | 3 | This is practical example of hexagonal architecture in NodeJS with Typescript. 4 | 5 | It's a super simple event management app. 6 | 7 | ## Structure 8 | 9 | - `/core` - application core, all business logic lives here. 10 | - `/primaryAdapters` - all adapters which are "use" our core to serve the user. 11 | In this example we support only REST API. 12 | - `/secondaryAdapters` - all driven adapters, which our core use via Ports. 13 | It is connection to 3rd party tools like DB, caches, notification service, SMS provider etc. 14 | 15 | ### Core 16 | 17 | `/core` has 3 main parts: 18 | 19 | - `/components` - actual app components with business logic. In DDD it usually called Bounded context. 20 | Each component is divided into `application` and `domain` layers. 21 | `application` is the layer with services which executes logic and communicates with outside world via Ports. 22 | `domain` layer stores our entities, values objects, domain services etc. 23 | Also component has `port` folder which stores component specific Ports. 24 | Common example of such port is Repository which communicates with DB (mostly with component specific tables). 25 | 26 | - `/port` - ports shared by components. 27 | It's an interfaces for 3rd party tools, like notification service, SMS service, search engine. 28 | 29 | - `/sharedKernel` - code that is shared among components. Mostly some utility functions. 30 | It should be extractable to an installable module if needed. 31 | 32 | 33 | ### Tests 34 | 35 | Testing becomes much easier with hexagonal architecture. 36 | 37 | I propose 3 types of test which suits different purposes: 38 | 39 | - `unit` - good old unit tests. Test for the smallest parts of application - services. 40 | Test if your core works properly. 41 | 42 | - `integration` - tests for secondary adapters to check if your integrations with 3rd part tools works. 43 | 44 | - `acceptance` - kinda integration tests. This is type of test where you test whole app flow with mocked secondary adapters. 45 | You start from primary adapter, e.g. sending http request and test how your components works together. 46 | Mocking secondary adapters makes them fast to execute. They are harder to write, because you may need a lot of mocks, 47 | so recommended to write them for core applications flows. 48 | -------------------------------------------------------------------------------- /env/.env.example: -------------------------------------------------------------------------------- 1 | PORT= 2 | HOST= 3 | 4 | ### Typeorm config. More details: https://github.com/typeorm/typeorm/blob/master/docs/using-ormconfig.md#using-environment-variables ### 5 | 6 | TYPEORM_CONNECTION= 7 | 8 | TYPEORM_HOST= 9 | TYPEORM_PORT= 10 | TYPEORM_USERNAME= 11 | TYPEORM_PASSWORD= 12 | TYPEORM_DATABASE= 13 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsfest-2019-demo-hexagonal", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.5.5", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", 10 | "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.0.0" 14 | } 15 | }, 16 | "@babel/generator": { 17 | "version": "7.5.5", 18 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", 19 | "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", 20 | "dev": true, 21 | "requires": { 22 | "@babel/types": "^7.5.5", 23 | "jsesc": "^2.5.1", 24 | "lodash": "^4.17.13", 25 | "source-map": "^0.5.0", 26 | "trim-right": "^1.0.1" 27 | } 28 | }, 29 | "@babel/helper-function-name": { 30 | "version": "7.1.0", 31 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", 32 | "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", 33 | "dev": true, 34 | "requires": { 35 | "@babel/helper-get-function-arity": "^7.0.0", 36 | "@babel/template": "^7.1.0", 37 | "@babel/types": "^7.0.0" 38 | } 39 | }, 40 | "@babel/helper-get-function-arity": { 41 | "version": "7.0.0", 42 | "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", 43 | "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", 44 | "dev": true, 45 | "requires": { 46 | "@babel/types": "^7.0.0" 47 | } 48 | }, 49 | "@babel/helper-split-export-declaration": { 50 | "version": "7.4.4", 51 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", 52 | "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", 53 | "dev": true, 54 | "requires": { 55 | "@babel/types": "^7.4.4" 56 | } 57 | }, 58 | "@babel/highlight": { 59 | "version": "7.5.0", 60 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", 61 | "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", 62 | "dev": true, 63 | "requires": { 64 | "chalk": "^2.0.0", 65 | "esutils": "^2.0.2", 66 | "js-tokens": "^4.0.0" 67 | } 68 | }, 69 | "@babel/parser": { 70 | "version": "7.5.5", 71 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", 72 | "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", 73 | "dev": true 74 | }, 75 | "@babel/template": { 76 | "version": "7.4.4", 77 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", 78 | "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", 79 | "dev": true, 80 | "requires": { 81 | "@babel/code-frame": "^7.0.0", 82 | "@babel/parser": "^7.4.4", 83 | "@babel/types": "^7.4.4" 84 | } 85 | }, 86 | "@babel/traverse": { 87 | "version": "7.5.5", 88 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", 89 | "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", 90 | "dev": true, 91 | "requires": { 92 | "@babel/code-frame": "^7.5.5", 93 | "@babel/generator": "^7.5.5", 94 | "@babel/helper-function-name": "^7.1.0", 95 | "@babel/helper-split-export-declaration": "^7.4.4", 96 | "@babel/parser": "^7.5.5", 97 | "@babel/types": "^7.5.5", 98 | "debug": "^4.1.0", 99 | "globals": "^11.1.0", 100 | "lodash": "^4.17.13" 101 | }, 102 | "dependencies": { 103 | "debug": { 104 | "version": "4.1.1", 105 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 106 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 107 | "dev": true, 108 | "requires": { 109 | "ms": "^2.1.1" 110 | } 111 | }, 112 | "ms": { 113 | "version": "2.1.2", 114 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 115 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 116 | "dev": true 117 | } 118 | } 119 | }, 120 | "@babel/types": { 121 | "version": "7.5.5", 122 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", 123 | "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", 124 | "dev": true, 125 | "requires": { 126 | "esutils": "^2.0.2", 127 | "lodash": "^4.17.13", 128 | "to-fast-properties": "^2.0.0" 129 | } 130 | }, 131 | "@sinonjs/commons": { 132 | "version": "1.6.0", 133 | "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz", 134 | "integrity": "sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg==", 135 | "dev": true, 136 | "requires": { 137 | "type-detect": "4.0.8" 138 | } 139 | }, 140 | "@sinonjs/formatio": { 141 | "version": "3.2.1", 142 | "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", 143 | "integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==", 144 | "dev": true, 145 | "requires": { 146 | "@sinonjs/commons": "^1", 147 | "@sinonjs/samsam": "^3.1.0" 148 | } 149 | }, 150 | "@sinonjs/samsam": { 151 | "version": "3.3.3", 152 | "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", 153 | "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", 154 | "dev": true, 155 | "requires": { 156 | "@sinonjs/commons": "^1.3.0", 157 | "array-from": "^2.1.1", 158 | "lodash": "^4.17.15" 159 | } 160 | }, 161 | "@sinonjs/text-encoding": { 162 | "version": "0.7.1", 163 | "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", 164 | "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", 165 | "dev": true 166 | }, 167 | "@types/body-parser": { 168 | "version": "1.17.1", 169 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", 170 | "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", 171 | "dev": true, 172 | "requires": { 173 | "@types/connect": "*", 174 | "@types/node": "*" 175 | } 176 | }, 177 | "@types/caseless": { 178 | "version": "0.12.2", 179 | "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", 180 | "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", 181 | "dev": true 182 | }, 183 | "@types/chai": { 184 | "version": "4.2.0", 185 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.0.tgz", 186 | "integrity": "sha512-zw8UvoBEImn392tLjxoavuonblX/4Yb9ha4KBU10FirCfwgzhKO0dvyJSF9ByxV1xK1r2AgnAi/tvQaLgxQqxA==", 187 | "dev": true 188 | }, 189 | "@types/chai-http": { 190 | "version": "4.2.0", 191 | "resolved": "https://registry.npmjs.org/@types/chai-http/-/chai-http-4.2.0.tgz", 192 | "integrity": "sha512-yb8+55cAuiVB7/yfnHIME5Ppw0LMUE6QFbuBFk5LewleI5pvYvtoXIEUAst3z3m0wZ2hLp1XeqllzffOSJSQuA==", 193 | "dev": true, 194 | "requires": { 195 | "chai-http": "*" 196 | } 197 | }, 198 | "@types/connect": { 199 | "version": "3.4.32", 200 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", 201 | "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", 202 | "dev": true, 203 | "requires": { 204 | "@types/node": "*" 205 | } 206 | }, 207 | "@types/cookiejar": { 208 | "version": "2.1.1", 209 | "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.1.tgz", 210 | "integrity": "sha512-aRnpPa7ysx3aNW60hTiCtLHlQaIFsXFCgQlpakNgDNVFzbtusSY8PwjAQgRWfSk0ekNoBjO51eQRB6upA9uuyw==", 211 | "dev": true 212 | }, 213 | "@types/dockerode": { 214 | "version": "2.5.20", 215 | "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-2.5.20.tgz", 216 | "integrity": "sha512-g2eM9q+pur7iZc897K/OSq8sCL7VdVcCPzNkdeTukUokfvgl3TaP+nT7G8BMpnSSojrJFKl7VdTciP7hbVgfKA==", 217 | "dev": true, 218 | "requires": { 219 | "@types/node": "*" 220 | } 221 | }, 222 | "@types/dotenv": { 223 | "version": "6.1.1", 224 | "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz", 225 | "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==", 226 | "dev": true, 227 | "requires": { 228 | "@types/node": "*" 229 | } 230 | }, 231 | "@types/express": { 232 | "version": "4.17.1", 233 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.1.tgz", 234 | "integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==", 235 | "dev": true, 236 | "requires": { 237 | "@types/body-parser": "*", 238 | "@types/express-serve-static-core": "*", 239 | "@types/serve-static": "*" 240 | } 241 | }, 242 | "@types/express-serve-static-core": { 243 | "version": "4.16.9", 244 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz", 245 | "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", 246 | "dev": true, 247 | "requires": { 248 | "@types/node": "*", 249 | "@types/range-parser": "*" 250 | } 251 | }, 252 | "@types/mime": { 253 | "version": "2.0.1", 254 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", 255 | "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", 256 | "dev": true 257 | }, 258 | "@types/mocha": { 259 | "version": "5.2.7", 260 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", 261 | "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", 262 | "dev": true 263 | }, 264 | "@types/node": { 265 | "version": "10.14.16", 266 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.16.tgz", 267 | "integrity": "sha512-/opXIbfn0P+VLt+N8DE4l8Mn8rbhiJgabU96ZJ0p9mxOkIks5gh6RUnpHak7Yh0SFkyjO/ODbxsQQPV2bpMmyA==", 268 | "dev": true 269 | }, 270 | "@types/pg": { 271 | "version": "7.11.0", 272 | "resolved": "https://registry.npmjs.org/@types/pg/-/pg-7.11.0.tgz", 273 | "integrity": "sha512-wXduaNIDQp7w9ediwIRAH+FpdgtOlOwFVlYe9DtBPDczxcHgfb0blLNR7yYVNUMhspC0xOLykOvMDHavbO0Sxg==", 274 | "dev": true, 275 | "requires": { 276 | "@types/node": "*", 277 | "@types/pg-types": "*" 278 | } 279 | }, 280 | "@types/pg-types": { 281 | "version": "1.11.4", 282 | "resolved": "https://registry.npmjs.org/@types/pg-types/-/pg-types-1.11.4.tgz", 283 | "integrity": "sha512-WdIiQmE347LGc1Vq3Ki8sk3iyCuLgnccqVzgxek6gEHp2H0p3MQ3jniIHt+bRODXKju4kNQ+mp53lmP5+/9moQ==", 284 | "dev": true, 285 | "requires": { 286 | "moment": ">=2.14.0" 287 | } 288 | }, 289 | "@types/pino": { 290 | "version": "5.8.9", 291 | "resolved": "https://registry.npmjs.org/@types/pino/-/pino-5.8.9.tgz", 292 | "integrity": "sha512-ybOTk+Ax54aKKHy4ZMDAVYBzuRhHn5NTGVyFTdH6vDrjTj3sQnP+TAlIJaKKbKu9J8NioRrj1kUGKDtvPPesmA==", 293 | "dev": true, 294 | "requires": { 295 | "@types/node": "*", 296 | "@types/pino-std-serializers": "*", 297 | "@types/sonic-boom": "*" 298 | } 299 | }, 300 | "@types/pino-std-serializers": { 301 | "version": "2.4.0", 302 | "resolved": "https://registry.npmjs.org/@types/pino-std-serializers/-/pino-std-serializers-2.4.0.tgz", 303 | "integrity": "sha512-eAdu+NW1IkCdmp85SnhyKha+OOREQMT9lXaoICQxa7bhSauRiLzu3WSNt9Mf2piuJvWeXF/G0hGWHr63xNpIRA==", 304 | "dev": true 305 | }, 306 | "@types/ramda": { 307 | "version": "0.25.51", 308 | "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.25.51.tgz", 309 | "integrity": "sha512-xcmtfHIgF9SYjhGdsZR1nQslxG4hu0cIpFfLQ4CWdw3KzHvl7ki1AzFLQUkbDTG42ZN3ZsQfdRzXRlkAvbIy5Q==", 310 | "dev": true 311 | }, 312 | "@types/range-parser": { 313 | "version": "1.2.3", 314 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", 315 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", 316 | "dev": true 317 | }, 318 | "@types/request": { 319 | "version": "2.48.2", 320 | "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.2.tgz", 321 | "integrity": "sha512-gP+PSFXAXMrd5PcD7SqHeUjdGshAI8vKQ3+AvpQr3ht9iQea+59LOKvKITcQI+Lg+1EIkDP6AFSBUJPWG8GDyA==", 322 | "dev": true, 323 | "requires": { 324 | "@types/caseless": "*", 325 | "@types/node": "*", 326 | "@types/tough-cookie": "*", 327 | "form-data": "^2.5.0" 328 | } 329 | }, 330 | "@types/request-promise-native": { 331 | "version": "1.0.16", 332 | "resolved": "https://registry.npmjs.org/@types/request-promise-native/-/request-promise-native-1.0.16.tgz", 333 | "integrity": "sha512-gbLf6cg1XGBU8BObOgs5VkCQo5JFz2GstgZjyE4FRbig/jiCEdiynu2fCzJlw3qYPuoj59spKnvuRLN4PsMvhA==", 334 | "dev": true, 335 | "requires": { 336 | "@types/request": "*" 337 | } 338 | }, 339 | "@types/serve-static": { 340 | "version": "1.13.3", 341 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", 342 | "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", 343 | "dev": true, 344 | "requires": { 345 | "@types/express-serve-static-core": "*", 346 | "@types/mime": "*" 347 | } 348 | }, 349 | "@types/sinon": { 350 | "version": "5.0.7", 351 | "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-5.0.7.tgz", 352 | "integrity": "sha512-opwMHufhUwkn/UUDk35LDbKJpA2VBsZT8WLU8NjayvRLGPxQkN+8XmfC2Xl35MAscBE8469koLLBjaI3XLEIww==", 353 | "dev": true 354 | }, 355 | "@types/sonic-boom": { 356 | "version": "0.7.0", 357 | "resolved": "https://registry.npmjs.org/@types/sonic-boom/-/sonic-boom-0.7.0.tgz", 358 | "integrity": "sha512-AfqR0fZMoUXUNwusgXKxcE9DPlHNDHQp6nKYUd4PSRpLobF5CCevSpyTEBcVZreqaWKCnGBr9KI1fHMTttoB7A==", 359 | "dev": true, 360 | "requires": { 361 | "@types/node": "*" 362 | } 363 | }, 364 | "@types/superagent": { 365 | "version": "3.8.7", 366 | "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-3.8.7.tgz", 367 | "integrity": "sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA==", 368 | "dev": true, 369 | "requires": { 370 | "@types/cookiejar": "*", 371 | "@types/node": "*" 372 | } 373 | }, 374 | "@types/tough-cookie": { 375 | "version": "2.3.5", 376 | "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", 377 | "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==", 378 | "dev": true 379 | }, 380 | "JSONStream": { 381 | "version": "1.3.2", 382 | "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", 383 | "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", 384 | "dev": true, 385 | "requires": { 386 | "jsonparse": "^1.2.0", 387 | "through": ">=2.2.7 <3" 388 | } 389 | }, 390 | "accepts": { 391 | "version": "1.3.7", 392 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 393 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 394 | "requires": { 395 | "mime-types": "~2.1.24", 396 | "negotiator": "0.6.2" 397 | } 398 | }, 399 | "ansi-regex": { 400 | "version": "4.1.0", 401 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 402 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" 403 | }, 404 | "ansi-styles": { 405 | "version": "3.2.1", 406 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 407 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 408 | "requires": { 409 | "color-convert": "^1.9.0" 410 | } 411 | }, 412 | "any-promise": { 413 | "version": "1.3.0", 414 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 415 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" 416 | }, 417 | "app-root-path": { 418 | "version": "2.2.1", 419 | "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", 420 | "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==" 421 | }, 422 | "append-transform": { 423 | "version": "1.0.0", 424 | "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", 425 | "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", 426 | "dev": true, 427 | "requires": { 428 | "default-require-extensions": "^2.0.0" 429 | } 430 | }, 431 | "archy": { 432 | "version": "1.0.0", 433 | "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", 434 | "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", 435 | "dev": true 436 | }, 437 | "argparse": { 438 | "version": "1.0.10", 439 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 440 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 441 | "requires": { 442 | "sprintf-js": "~1.0.2" 443 | } 444 | }, 445 | "args": { 446 | "version": "5.0.1", 447 | "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz", 448 | "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==", 449 | "dev": true, 450 | "requires": { 451 | "camelcase": "5.0.0", 452 | "chalk": "2.4.2", 453 | "leven": "2.1.0", 454 | "mri": "1.1.4" 455 | }, 456 | "dependencies": { 457 | "camelcase": { 458 | "version": "5.0.0", 459 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", 460 | "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", 461 | "dev": true 462 | } 463 | } 464 | }, 465 | "array-flatten": { 466 | "version": "1.1.1", 467 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 468 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 469 | }, 470 | "array-from": { 471 | "version": "2.1.1", 472 | "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", 473 | "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", 474 | "dev": true 475 | }, 476 | "arrify": { 477 | "version": "1.0.1", 478 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 479 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 480 | "dev": true 481 | }, 482 | "assertion-error": { 483 | "version": "1.1.0", 484 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 485 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 486 | "dev": true 487 | }, 488 | "asynckit": { 489 | "version": "0.4.0", 490 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 491 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 492 | "dev": true 493 | }, 494 | "balanced-match": { 495 | "version": "1.0.0", 496 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 497 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 498 | }, 499 | "base64-js": { 500 | "version": "1.3.1", 501 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 502 | "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" 503 | }, 504 | "bl": { 505 | "version": "1.2.2", 506 | "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", 507 | "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", 508 | "dev": true, 509 | "requires": { 510 | "readable-stream": "^2.3.5", 511 | "safe-buffer": "^5.1.1" 512 | } 513 | }, 514 | "body-parser": { 515 | "version": "1.19.0", 516 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 517 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 518 | "requires": { 519 | "bytes": "3.1.0", 520 | "content-type": "~1.0.4", 521 | "debug": "2.6.9", 522 | "depd": "~1.1.2", 523 | "http-errors": "1.7.2", 524 | "iconv-lite": "0.4.24", 525 | "on-finished": "~2.3.0", 526 | "qs": "6.7.0", 527 | "raw-body": "2.4.0", 528 | "type-is": "~1.6.17" 529 | } 530 | }, 531 | "brace-expansion": { 532 | "version": "1.1.11", 533 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 534 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 535 | "requires": { 536 | "balanced-match": "^1.0.0", 537 | "concat-map": "0.0.1" 538 | } 539 | }, 540 | "browser-stdout": { 541 | "version": "1.3.1", 542 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 543 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 544 | "dev": true 545 | }, 546 | "buffer": { 547 | "version": "5.4.0", 548 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.0.tgz", 549 | "integrity": "sha512-Xpgy0IwHK2N01ncykXTy6FpCWuM+CJSHoPVBLyNqyrWxsedpLvwsYUhf0ME3WRFNUhos0dMamz9cOS/xRDtU5g==", 550 | "requires": { 551 | "base64-js": "^1.0.2", 552 | "ieee754": "^1.1.4" 553 | } 554 | }, 555 | "buffer-alloc": { 556 | "version": "1.2.0", 557 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 558 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 559 | "dev": true, 560 | "requires": { 561 | "buffer-alloc-unsafe": "^1.1.0", 562 | "buffer-fill": "^1.0.0" 563 | } 564 | }, 565 | "buffer-alloc-unsafe": { 566 | "version": "1.1.0", 567 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 568 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", 569 | "dev": true 570 | }, 571 | "buffer-fill": { 572 | "version": "1.0.0", 573 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 574 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", 575 | "dev": true 576 | }, 577 | "buffer-from": { 578 | "version": "1.1.1", 579 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 580 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 581 | "dev": true 582 | }, 583 | "buffer-writer": { 584 | "version": "2.0.0", 585 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", 586 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" 587 | }, 588 | "bytes": { 589 | "version": "3.1.0", 590 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 591 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 592 | }, 593 | "caching-transform": { 594 | "version": "3.0.2", 595 | "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", 596 | "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", 597 | "dev": true, 598 | "requires": { 599 | "hasha": "^3.0.0", 600 | "make-dir": "^2.0.0", 601 | "package-hash": "^3.0.0", 602 | "write-file-atomic": "^2.4.2" 603 | } 604 | }, 605 | "camelcase": { 606 | "version": "5.3.1", 607 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 608 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 609 | }, 610 | "chai": { 611 | "version": "4.2.0", 612 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", 613 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", 614 | "dev": true, 615 | "requires": { 616 | "assertion-error": "^1.1.0", 617 | "check-error": "^1.0.2", 618 | "deep-eql": "^3.0.1", 619 | "get-func-name": "^2.0.0", 620 | "pathval": "^1.1.0", 621 | "type-detect": "^4.0.5" 622 | } 623 | }, 624 | "chai-http": { 625 | "version": "4.3.0", 626 | "resolved": "https://registry.npmjs.org/chai-http/-/chai-http-4.3.0.tgz", 627 | "integrity": "sha512-zFTxlN7HLMv+7+SPXZdkd5wUlK+KxH6Q7bIEMiEx0FK3zuuMqL7cwICAQ0V1+yYRozBburYuxN1qZstgHpFZQg==", 628 | "dev": true, 629 | "requires": { 630 | "@types/chai": "4", 631 | "@types/superagent": "^3.8.3", 632 | "cookiejar": "^2.1.1", 633 | "is-ip": "^2.0.0", 634 | "methods": "^1.1.2", 635 | "qs": "^6.5.1", 636 | "superagent": "^3.7.0" 637 | } 638 | }, 639 | "chalk": { 640 | "version": "2.4.2", 641 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 642 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 643 | "requires": { 644 | "ansi-styles": "^3.2.1", 645 | "escape-string-regexp": "^1.0.5", 646 | "supports-color": "^5.3.0" 647 | } 648 | }, 649 | "check-error": { 650 | "version": "1.0.2", 651 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 652 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 653 | "dev": true 654 | }, 655 | "chownr": { 656 | "version": "1.1.2", 657 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", 658 | "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", 659 | "dev": true 660 | }, 661 | "class-transformer": { 662 | "version": "0.1.10", 663 | "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.1.10.tgz", 664 | "integrity": "sha512-QiNdUxEvTBiUtc0KiapGVHhgaqGQVEhOfL9UEBnb9xRfcwmDJT5ijIDwcwJUTwXaT/kGvZZB4JCGsiuR5adX6g==" 665 | }, 666 | "class-validator": { 667 | "version": "0.9.1", 668 | "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.9.1.tgz", 669 | "integrity": "sha512-3wApflrd3ywVZyx4jaasGoFt8pmo4aGLPPAEKCKCsTRWVGPilahD88q3jQjRQwja50rl9a7rsP5LAxJYwGK8/Q==", 670 | "requires": { 671 | "google-libphonenumber": "^3.1.6", 672 | "validator": "10.4.0" 673 | } 674 | }, 675 | "class-validator-jsonschema": { 676 | "version": "1.3.0", 677 | "resolved": "https://registry.npmjs.org/class-validator-jsonschema/-/class-validator-jsonschema-1.3.0.tgz", 678 | "integrity": "sha512-W7xWPfmq6zfp/ZE1s7VWJp5uI2SbheCQ3PdBz+R093VBmQSTy8+wYArn3Dw1UWrE51so5Lv5o8NzdkTh4shGzA==", 679 | "requires": { 680 | "debug": "^4.1.1", 681 | "lodash": "^4.17.11", 682 | "openapi3-ts": "^1.3.0", 683 | "reflect-metadata": "^0.1.13", 684 | "tslib": "^1.10.0" 685 | }, 686 | "dependencies": { 687 | "debug": { 688 | "version": "4.1.1", 689 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 690 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 691 | "requires": { 692 | "ms": "^2.1.1" 693 | } 694 | }, 695 | "ms": { 696 | "version": "2.1.2", 697 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 698 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 699 | } 700 | } 701 | }, 702 | "cli-highlight": { 703 | "version": "2.1.1", 704 | "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.1.tgz", 705 | "integrity": "sha512-0y0VlNmdD99GXZHYnvrQcmHxP8Bi6T00qucGgBgGv4kJ0RyDthNnnFPupHV7PYv/OXSVk+azFbOeaW6+vGmx9A==", 706 | "requires": { 707 | "chalk": "^2.3.0", 708 | "highlight.js": "^9.6.0", 709 | "mz": "^2.4.0", 710 | "parse5": "^4.0.0", 711 | "yargs": "^13.0.0" 712 | } 713 | }, 714 | "cliui": { 715 | "version": "5.0.0", 716 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 717 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 718 | "requires": { 719 | "string-width": "^3.1.0", 720 | "strip-ansi": "^5.2.0", 721 | "wrap-ansi": "^5.1.0" 722 | } 723 | }, 724 | "color-convert": { 725 | "version": "1.9.3", 726 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 727 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 728 | "requires": { 729 | "color-name": "1.1.3" 730 | } 731 | }, 732 | "color-name": { 733 | "version": "1.1.3", 734 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 735 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 736 | }, 737 | "combined-stream": { 738 | "version": "1.0.8", 739 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 740 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 741 | "dev": true, 742 | "requires": { 743 | "delayed-stream": "~1.0.0" 744 | } 745 | }, 746 | "commander": { 747 | "version": "2.15.1", 748 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 749 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 750 | "dev": true 751 | }, 752 | "commondir": { 753 | "version": "1.0.1", 754 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 755 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", 756 | "dev": true 757 | }, 758 | "component-emitter": { 759 | "version": "1.3.0", 760 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", 761 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", 762 | "dev": true 763 | }, 764 | "concat-map": { 765 | "version": "0.0.1", 766 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 767 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 768 | }, 769 | "concat-stream": { 770 | "version": "1.6.2", 771 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 772 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 773 | "dev": true, 774 | "requires": { 775 | "buffer-from": "^1.0.0", 776 | "inherits": "^2.0.3", 777 | "readable-stream": "^2.2.2", 778 | "typedarray": "^0.0.6" 779 | } 780 | }, 781 | "content-disposition": { 782 | "version": "0.5.3", 783 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 784 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 785 | "requires": { 786 | "safe-buffer": "5.1.2" 787 | } 788 | }, 789 | "content-type": { 790 | "version": "1.0.4", 791 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 792 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 793 | }, 794 | "convert-source-map": { 795 | "version": "1.6.0", 796 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", 797 | "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", 798 | "dev": true, 799 | "requires": { 800 | "safe-buffer": "~5.1.1" 801 | } 802 | }, 803 | "cookie": { 804 | "version": "0.4.0", 805 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 806 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 807 | }, 808 | "cookie-signature": { 809 | "version": "1.0.6", 810 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 811 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 812 | }, 813 | "cookiejar": { 814 | "version": "2.1.2", 815 | "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", 816 | "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", 817 | "dev": true 818 | }, 819 | "core-util-is": { 820 | "version": "1.0.2", 821 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 822 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 823 | "dev": true 824 | }, 825 | "cp-file": { 826 | "version": "6.2.0", 827 | "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", 828 | "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", 829 | "dev": true, 830 | "requires": { 831 | "graceful-fs": "^4.1.2", 832 | "make-dir": "^2.0.0", 833 | "nested-error-stacks": "^2.0.0", 834 | "pify": "^4.0.1", 835 | "safe-buffer": "^5.0.1" 836 | } 837 | }, 838 | "cross-env": { 839 | "version": "5.2.0", 840 | "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.0.tgz", 841 | "integrity": "sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==", 842 | "dev": true, 843 | "requires": { 844 | "cross-spawn": "^6.0.5", 845 | "is-windows": "^1.0.0" 846 | } 847 | }, 848 | "cross-spawn": { 849 | "version": "6.0.5", 850 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 851 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 852 | "dev": true, 853 | "requires": { 854 | "nice-try": "^1.0.4", 855 | "path-key": "^2.0.1", 856 | "semver": "^5.5.0", 857 | "shebang-command": "^1.2.0", 858 | "which": "^1.2.9" 859 | }, 860 | "dependencies": { 861 | "semver": { 862 | "version": "5.7.1", 863 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 864 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 865 | "dev": true 866 | } 867 | } 868 | }, 869 | "dateformat": { 870 | "version": "3.0.3", 871 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", 872 | "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", 873 | "dev": true 874 | }, 875 | "debug": { 876 | "version": "2.6.9", 877 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 878 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 879 | "requires": { 880 | "ms": "2.0.0" 881 | } 882 | }, 883 | "decamelize": { 884 | "version": "1.2.0", 885 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 886 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 887 | }, 888 | "deep-eql": { 889 | "version": "3.0.1", 890 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 891 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 892 | "dev": true, 893 | "requires": { 894 | "type-detect": "^4.0.0" 895 | } 896 | }, 897 | "default-require-extensions": { 898 | "version": "2.0.0", 899 | "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", 900 | "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", 901 | "dev": true, 902 | "requires": { 903 | "strip-bom": "^3.0.0" 904 | } 905 | }, 906 | "delayed-stream": { 907 | "version": "1.0.0", 908 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 909 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 910 | "dev": true 911 | }, 912 | "depd": { 913 | "version": "1.1.2", 914 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 915 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 916 | }, 917 | "destroy": { 918 | "version": "1.0.4", 919 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 920 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 921 | }, 922 | "diff": { 923 | "version": "3.5.0", 924 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 925 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 926 | "dev": true 927 | }, 928 | "docker-modem": { 929 | "version": "1.0.9", 930 | "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-1.0.9.tgz", 931 | "integrity": "sha512-lVjqCSCIAUDZPAZIeyM125HXfNvOmYYInciphNrLrylUtKyW66meAjSPXWchKVzoIYZx69TPnAepVSSkeawoIw==", 932 | "dev": true, 933 | "requires": { 934 | "JSONStream": "1.3.2", 935 | "debug": "^3.2.6", 936 | "readable-stream": "~1.0.26-4", 937 | "split-ca": "^1.0.0" 938 | }, 939 | "dependencies": { 940 | "debug": { 941 | "version": "3.2.6", 942 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 943 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 944 | "dev": true, 945 | "requires": { 946 | "ms": "^2.1.1" 947 | } 948 | }, 949 | "isarray": { 950 | "version": "0.0.1", 951 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 952 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 953 | "dev": true 954 | }, 955 | "ms": { 956 | "version": "2.1.2", 957 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 958 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 959 | "dev": true 960 | }, 961 | "readable-stream": { 962 | "version": "1.0.34", 963 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 964 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 965 | "dev": true, 966 | "requires": { 967 | "core-util-is": "~1.0.0", 968 | "inherits": "~2.0.1", 969 | "isarray": "0.0.1", 970 | "string_decoder": "~0.10.x" 971 | } 972 | }, 973 | "string_decoder": { 974 | "version": "0.10.31", 975 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 976 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 977 | "dev": true 978 | } 979 | } 980 | }, 981 | "dockerode": { 982 | "version": "2.5.8", 983 | "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-2.5.8.tgz", 984 | "integrity": "sha512-+7iOUYBeDTScmOmQqpUYQaE7F4vvIt6+gIZNHWhqAQEI887tiPFB9OvXI/HzQYqfUNvukMK+9myLW63oTJPZpw==", 985 | "dev": true, 986 | "requires": { 987 | "concat-stream": "~1.6.2", 988 | "docker-modem": "^1.0.8", 989 | "tar-fs": "~1.16.3" 990 | } 991 | }, 992 | "dotenv": { 993 | "version": "6.2.0", 994 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", 995 | "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==" 996 | }, 997 | "ee-first": { 998 | "version": "1.1.1", 999 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 1000 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 1001 | }, 1002 | "emoji-regex": { 1003 | "version": "7.0.3", 1004 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 1005 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" 1006 | }, 1007 | "encodeurl": { 1008 | "version": "1.0.2", 1009 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 1010 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 1011 | }, 1012 | "end-of-stream": { 1013 | "version": "1.4.1", 1014 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 1015 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 1016 | "dev": true, 1017 | "requires": { 1018 | "once": "^1.4.0" 1019 | } 1020 | }, 1021 | "error-ex": { 1022 | "version": "1.3.2", 1023 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 1024 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 1025 | "dev": true, 1026 | "requires": { 1027 | "is-arrayish": "^0.2.1" 1028 | } 1029 | }, 1030 | "es6-error": { 1031 | "version": "4.1.1", 1032 | "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", 1033 | "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", 1034 | "dev": true 1035 | }, 1036 | "escape-html": { 1037 | "version": "1.0.3", 1038 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 1039 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 1040 | }, 1041 | "escape-string-regexp": { 1042 | "version": "1.0.5", 1043 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 1044 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 1045 | }, 1046 | "esprima": { 1047 | "version": "4.0.1", 1048 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 1049 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 1050 | }, 1051 | "esutils": { 1052 | "version": "2.0.3", 1053 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 1054 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 1055 | "dev": true 1056 | }, 1057 | "etag": { 1058 | "version": "1.8.1", 1059 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 1060 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 1061 | }, 1062 | "express": { 1063 | "version": "4.17.1", 1064 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 1065 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 1066 | "requires": { 1067 | "accepts": "~1.3.7", 1068 | "array-flatten": "1.1.1", 1069 | "body-parser": "1.19.0", 1070 | "content-disposition": "0.5.3", 1071 | "content-type": "~1.0.4", 1072 | "cookie": "0.4.0", 1073 | "cookie-signature": "1.0.6", 1074 | "debug": "2.6.9", 1075 | "depd": "~1.1.2", 1076 | "encodeurl": "~1.0.2", 1077 | "escape-html": "~1.0.3", 1078 | "etag": "~1.8.1", 1079 | "finalhandler": "~1.1.2", 1080 | "fresh": "0.5.2", 1081 | "merge-descriptors": "1.0.1", 1082 | "methods": "~1.1.2", 1083 | "on-finished": "~2.3.0", 1084 | "parseurl": "~1.3.3", 1085 | "path-to-regexp": "0.1.7", 1086 | "proxy-addr": "~2.0.5", 1087 | "qs": "6.7.0", 1088 | "range-parser": "~1.2.1", 1089 | "safe-buffer": "5.1.2", 1090 | "send": "0.17.1", 1091 | "serve-static": "1.14.1", 1092 | "setprototypeof": "1.1.1", 1093 | "statuses": "~1.5.0", 1094 | "type-is": "~1.6.18", 1095 | "utils-merge": "1.0.1", 1096 | "vary": "~1.1.2" 1097 | } 1098 | }, 1099 | "express-pino-logger": { 1100 | "version": "4.0.0", 1101 | "resolved": "https://registry.npmjs.org/express-pino-logger/-/express-pino-logger-4.0.0.tgz", 1102 | "integrity": "sha512-BTJwjQXMSR6tFiyvTOOr6aosJkJOuJpW0mXE+icv3ae/0WXBGnLaumINGHJvWMuDO1RSLHBLfRrJaghMjMhVrg==", 1103 | "requires": { 1104 | "pino-http": "^4.0.0" 1105 | } 1106 | }, 1107 | "extend": { 1108 | "version": "3.0.2", 1109 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 1110 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 1111 | "dev": true 1112 | }, 1113 | "fast-json-parse": { 1114 | "version": "1.0.3", 1115 | "resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz", 1116 | "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==", 1117 | "dev": true 1118 | }, 1119 | "fast-redact": { 1120 | "version": "1.5.0", 1121 | "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-1.5.0.tgz", 1122 | "integrity": "sha512-Afo61CgUjkzdvOKDHn08qnZ0kwck38AOGcMlvSGzvJbIab6soAP5rdoQayecGCDsD69AiF9vJBXyq31eoEO2tQ==" 1123 | }, 1124 | "fast-safe-stringify": { 1125 | "version": "2.0.6", 1126 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", 1127 | "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==" 1128 | }, 1129 | "figlet": { 1130 | "version": "1.2.3", 1131 | "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.2.3.tgz", 1132 | "integrity": "sha512-+F5zdvZ66j77b8x2KCPvWUHC0UCKUMWrewxmewgPlagp3wmDpcrHMbyv/ygq/6xoxBPGQA+UJU3SMoBzKoROQQ==" 1133 | }, 1134 | "finalhandler": { 1135 | "version": "1.1.2", 1136 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 1137 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 1138 | "requires": { 1139 | "debug": "2.6.9", 1140 | "encodeurl": "~1.0.2", 1141 | "escape-html": "~1.0.3", 1142 | "on-finished": "~2.3.0", 1143 | "parseurl": "~1.3.3", 1144 | "statuses": "~1.5.0", 1145 | "unpipe": "~1.0.0" 1146 | } 1147 | }, 1148 | "find-cache-dir": { 1149 | "version": "2.1.0", 1150 | "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", 1151 | "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", 1152 | "dev": true, 1153 | "requires": { 1154 | "commondir": "^1.0.1", 1155 | "make-dir": "^2.0.0", 1156 | "pkg-dir": "^3.0.0" 1157 | } 1158 | }, 1159 | "find-up": { 1160 | "version": "3.0.0", 1161 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 1162 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 1163 | "requires": { 1164 | "locate-path": "^3.0.0" 1165 | } 1166 | }, 1167 | "flatstr": { 1168 | "version": "1.0.12", 1169 | "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz", 1170 | "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" 1171 | }, 1172 | "foreground-child": { 1173 | "version": "1.5.6", 1174 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", 1175 | "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", 1176 | "dev": true, 1177 | "requires": { 1178 | "cross-spawn": "^4", 1179 | "signal-exit": "^3.0.0" 1180 | }, 1181 | "dependencies": { 1182 | "cross-spawn": { 1183 | "version": "4.0.2", 1184 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", 1185 | "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", 1186 | "dev": true, 1187 | "requires": { 1188 | "lru-cache": "^4.0.1", 1189 | "which": "^1.2.9" 1190 | } 1191 | } 1192 | } 1193 | }, 1194 | "form-data": { 1195 | "version": "2.5.0", 1196 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.0.tgz", 1197 | "integrity": "sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA==", 1198 | "dev": true, 1199 | "requires": { 1200 | "asynckit": "^0.4.0", 1201 | "combined-stream": "^1.0.6", 1202 | "mime-types": "^2.1.12" 1203 | } 1204 | }, 1205 | "formidable": { 1206 | "version": "1.2.1", 1207 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", 1208 | "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", 1209 | "dev": true 1210 | }, 1211 | "forwarded": { 1212 | "version": "0.1.2", 1213 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 1214 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 1215 | }, 1216 | "fresh": { 1217 | "version": "0.5.2", 1218 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 1219 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 1220 | }, 1221 | "fs-constants": { 1222 | "version": "1.0.0", 1223 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 1224 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 1225 | "dev": true 1226 | }, 1227 | "fs.realpath": { 1228 | "version": "1.0.0", 1229 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1230 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 1231 | }, 1232 | "get-caller-file": { 1233 | "version": "2.0.5", 1234 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1235 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" 1236 | }, 1237 | "get-func-name": { 1238 | "version": "2.0.0", 1239 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 1240 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 1241 | "dev": true 1242 | }, 1243 | "glob": { 1244 | "version": "7.1.4", 1245 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 1246 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 1247 | "requires": { 1248 | "fs.realpath": "^1.0.0", 1249 | "inflight": "^1.0.4", 1250 | "inherits": "2", 1251 | "minimatch": "^3.0.4", 1252 | "once": "^1.3.0", 1253 | "path-is-absolute": "^1.0.0" 1254 | } 1255 | }, 1256 | "globals": { 1257 | "version": "11.12.0", 1258 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 1259 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 1260 | "dev": true 1261 | }, 1262 | "google-libphonenumber": { 1263 | "version": "3.2.3", 1264 | "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.3.tgz", 1265 | "integrity": "sha512-8n4JyRptifaIRlHANKRlfqLR8fANm7+Q+1qvDuUsUeStSLtLGTVsZWe1llWDfgWTm1y07cEUyiRuNIv6cs2ovg==" 1266 | }, 1267 | "graceful-fs": { 1268 | "version": "4.2.2", 1269 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", 1270 | "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", 1271 | "dev": true 1272 | }, 1273 | "growl": { 1274 | "version": "1.10.5", 1275 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 1276 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 1277 | "dev": true 1278 | }, 1279 | "handlebars": { 1280 | "version": "4.1.2", 1281 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", 1282 | "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", 1283 | "dev": true, 1284 | "requires": { 1285 | "neo-async": "^2.6.0", 1286 | "optimist": "^0.6.1", 1287 | "source-map": "^0.6.1", 1288 | "uglify-js": "^3.1.4" 1289 | }, 1290 | "dependencies": { 1291 | "source-map": { 1292 | "version": "0.6.1", 1293 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1294 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1295 | "dev": true 1296 | } 1297 | } 1298 | }, 1299 | "has-ansi": { 1300 | "version": "2.0.0", 1301 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 1302 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 1303 | "requires": { 1304 | "ansi-regex": "^2.0.0" 1305 | }, 1306 | "dependencies": { 1307 | "ansi-regex": { 1308 | "version": "2.1.1", 1309 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 1310 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 1311 | } 1312 | } 1313 | }, 1314 | "has-flag": { 1315 | "version": "3.0.0", 1316 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1317 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 1318 | }, 1319 | "hasha": { 1320 | "version": "3.0.0", 1321 | "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", 1322 | "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", 1323 | "dev": true, 1324 | "requires": { 1325 | "is-stream": "^1.0.1" 1326 | } 1327 | }, 1328 | "he": { 1329 | "version": "1.1.1", 1330 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 1331 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 1332 | "dev": true 1333 | }, 1334 | "highlight.js": { 1335 | "version": "9.15.10", 1336 | "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz", 1337 | "integrity": "sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw==" 1338 | }, 1339 | "hosted-git-info": { 1340 | "version": "2.8.4", 1341 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", 1342 | "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", 1343 | "dev": true 1344 | }, 1345 | "http-errors": { 1346 | "version": "1.7.2", 1347 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 1348 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 1349 | "requires": { 1350 | "depd": "~1.1.2", 1351 | "inherits": "2.0.3", 1352 | "setprototypeof": "1.1.1", 1353 | "statuses": ">= 1.5.0 < 2", 1354 | "toidentifier": "1.0.0" 1355 | } 1356 | }, 1357 | "iconv-lite": { 1358 | "version": "0.4.24", 1359 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1360 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1361 | "requires": { 1362 | "safer-buffer": ">= 2.1.2 < 3" 1363 | } 1364 | }, 1365 | "ieee754": { 1366 | "version": "1.1.13", 1367 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 1368 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 1369 | }, 1370 | "imurmurhash": { 1371 | "version": "0.1.4", 1372 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1373 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 1374 | "dev": true 1375 | }, 1376 | "inflight": { 1377 | "version": "1.0.6", 1378 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1379 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1380 | "requires": { 1381 | "once": "^1.3.0", 1382 | "wrappy": "1" 1383 | } 1384 | }, 1385 | "inherits": { 1386 | "version": "2.0.3", 1387 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1388 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 1389 | }, 1390 | "inversify": { 1391 | "version": "5.0.1", 1392 | "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.1.tgz", 1393 | "integrity": "sha512-Ieh06s48WnEYGcqHepdsJUIJUXpwH5o5vodAX+DK2JA/gjy4EbEcQZxw+uFfzysmKjiLXGYwNG3qDZsKVMcINQ==" 1394 | }, 1395 | "ip-regex": { 1396 | "version": "2.1.0", 1397 | "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", 1398 | "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", 1399 | "dev": true 1400 | }, 1401 | "ipaddr.js": { 1402 | "version": "1.9.0", 1403 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 1404 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" 1405 | }, 1406 | "is-arrayish": { 1407 | "version": "0.2.1", 1408 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 1409 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 1410 | "dev": true 1411 | }, 1412 | "is-fullwidth-code-point": { 1413 | "version": "2.0.0", 1414 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1415 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 1416 | }, 1417 | "is-ip": { 1418 | "version": "2.0.0", 1419 | "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz", 1420 | "integrity": "sha1-aO6gfooKCpTC0IDdZ0xzGrKkYas=", 1421 | "dev": true, 1422 | "requires": { 1423 | "ip-regex": "^2.0.0" 1424 | } 1425 | }, 1426 | "is-stream": { 1427 | "version": "1.1.0", 1428 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 1429 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 1430 | "dev": true 1431 | }, 1432 | "is-windows": { 1433 | "version": "1.0.2", 1434 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", 1435 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", 1436 | "dev": true 1437 | }, 1438 | "isarray": { 1439 | "version": "1.0.0", 1440 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1441 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 1442 | "dev": true 1443 | }, 1444 | "isexe": { 1445 | "version": "2.0.0", 1446 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1447 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1448 | "dev": true 1449 | }, 1450 | "istanbul-lib-coverage": { 1451 | "version": "2.0.5", 1452 | "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", 1453 | "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", 1454 | "dev": true 1455 | }, 1456 | "istanbul-lib-hook": { 1457 | "version": "2.0.7", 1458 | "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", 1459 | "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", 1460 | "dev": true, 1461 | "requires": { 1462 | "append-transform": "^1.0.0" 1463 | } 1464 | }, 1465 | "istanbul-lib-instrument": { 1466 | "version": "3.3.0", 1467 | "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", 1468 | "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", 1469 | "dev": true, 1470 | "requires": { 1471 | "@babel/generator": "^7.4.0", 1472 | "@babel/parser": "^7.4.3", 1473 | "@babel/template": "^7.4.0", 1474 | "@babel/traverse": "^7.4.3", 1475 | "@babel/types": "^7.4.0", 1476 | "istanbul-lib-coverage": "^2.0.5", 1477 | "semver": "^6.0.0" 1478 | }, 1479 | "dependencies": { 1480 | "semver": { 1481 | "version": "6.3.0", 1482 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1483 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1484 | "dev": true 1485 | } 1486 | } 1487 | }, 1488 | "istanbul-lib-report": { 1489 | "version": "2.0.8", 1490 | "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", 1491 | "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", 1492 | "dev": true, 1493 | "requires": { 1494 | "istanbul-lib-coverage": "^2.0.5", 1495 | "make-dir": "^2.1.0", 1496 | "supports-color": "^6.1.0" 1497 | }, 1498 | "dependencies": { 1499 | "supports-color": { 1500 | "version": "6.1.0", 1501 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", 1502 | "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", 1503 | "dev": true, 1504 | "requires": { 1505 | "has-flag": "^3.0.0" 1506 | } 1507 | } 1508 | } 1509 | }, 1510 | "istanbul-lib-source-maps": { 1511 | "version": "3.0.6", 1512 | "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", 1513 | "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", 1514 | "dev": true, 1515 | "requires": { 1516 | "debug": "^4.1.1", 1517 | "istanbul-lib-coverage": "^2.0.5", 1518 | "make-dir": "^2.1.0", 1519 | "rimraf": "^2.6.3", 1520 | "source-map": "^0.6.1" 1521 | }, 1522 | "dependencies": { 1523 | "debug": { 1524 | "version": "4.1.1", 1525 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 1526 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 1527 | "dev": true, 1528 | "requires": { 1529 | "ms": "^2.1.1" 1530 | } 1531 | }, 1532 | "ms": { 1533 | "version": "2.1.2", 1534 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1535 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1536 | "dev": true 1537 | }, 1538 | "source-map": { 1539 | "version": "0.6.1", 1540 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1541 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1542 | "dev": true 1543 | } 1544 | } 1545 | }, 1546 | "istanbul-reports": { 1547 | "version": "2.2.6", 1548 | "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", 1549 | "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", 1550 | "dev": true, 1551 | "requires": { 1552 | "handlebars": "^4.1.2" 1553 | } 1554 | }, 1555 | "jmespath": { 1556 | "version": "0.15.0", 1557 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 1558 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", 1559 | "dev": true 1560 | }, 1561 | "js-tokens": { 1562 | "version": "4.0.0", 1563 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1564 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1565 | "dev": true 1566 | }, 1567 | "js-yaml": { 1568 | "version": "3.13.1", 1569 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 1570 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 1571 | "requires": { 1572 | "argparse": "^1.0.7", 1573 | "esprima": "^4.0.0" 1574 | } 1575 | }, 1576 | "jsesc": { 1577 | "version": "2.5.2", 1578 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 1579 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", 1580 | "dev": true 1581 | }, 1582 | "json-parse-better-errors": { 1583 | "version": "1.0.2", 1584 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 1585 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", 1586 | "dev": true 1587 | }, 1588 | "jsonparse": { 1589 | "version": "1.3.1", 1590 | "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", 1591 | "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", 1592 | "dev": true 1593 | }, 1594 | "just-extend": { 1595 | "version": "4.0.2", 1596 | "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", 1597 | "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", 1598 | "dev": true 1599 | }, 1600 | "leven": { 1601 | "version": "2.1.0", 1602 | "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", 1603 | "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", 1604 | "dev": true 1605 | }, 1606 | "load-json-file": { 1607 | "version": "4.0.0", 1608 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", 1609 | "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", 1610 | "dev": true, 1611 | "requires": { 1612 | "graceful-fs": "^4.1.2", 1613 | "parse-json": "^4.0.0", 1614 | "pify": "^3.0.0", 1615 | "strip-bom": "^3.0.0" 1616 | }, 1617 | "dependencies": { 1618 | "pify": { 1619 | "version": "3.0.0", 1620 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 1621 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 1622 | "dev": true 1623 | } 1624 | } 1625 | }, 1626 | "locate-path": { 1627 | "version": "3.0.0", 1628 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 1629 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 1630 | "requires": { 1631 | "p-locate": "^3.0.0", 1632 | "path-exists": "^3.0.0" 1633 | } 1634 | }, 1635 | "lodash": { 1636 | "version": "4.17.15", 1637 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 1638 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 1639 | }, 1640 | "lodash.flattendeep": { 1641 | "version": "4.4.0", 1642 | "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", 1643 | "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", 1644 | "dev": true 1645 | }, 1646 | "lolex": { 1647 | "version": "4.2.0", 1648 | "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", 1649 | "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", 1650 | "dev": true 1651 | }, 1652 | "lru-cache": { 1653 | "version": "4.1.5", 1654 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 1655 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 1656 | "dev": true, 1657 | "requires": { 1658 | "pseudomap": "^1.0.2", 1659 | "yallist": "^2.1.2" 1660 | } 1661 | }, 1662 | "make-dir": { 1663 | "version": "2.1.0", 1664 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", 1665 | "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", 1666 | "dev": true, 1667 | "requires": { 1668 | "pify": "^4.0.1", 1669 | "semver": "^5.6.0" 1670 | }, 1671 | "dependencies": { 1672 | "semver": { 1673 | "version": "5.7.1", 1674 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1675 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 1676 | "dev": true 1677 | } 1678 | } 1679 | }, 1680 | "make-error": { 1681 | "version": "1.3.5", 1682 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", 1683 | "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", 1684 | "dev": true 1685 | }, 1686 | "media-typer": { 1687 | "version": "0.3.0", 1688 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1689 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 1690 | }, 1691 | "merge-descriptors": { 1692 | "version": "1.0.1", 1693 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1694 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 1695 | }, 1696 | "merge-source-map": { 1697 | "version": "1.1.0", 1698 | "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", 1699 | "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", 1700 | "dev": true, 1701 | "requires": { 1702 | "source-map": "^0.6.1" 1703 | }, 1704 | "dependencies": { 1705 | "source-map": { 1706 | "version": "0.6.1", 1707 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1708 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1709 | "dev": true 1710 | } 1711 | } 1712 | }, 1713 | "methods": { 1714 | "version": "1.1.2", 1715 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1716 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 1717 | }, 1718 | "mime": { 1719 | "version": "1.6.0", 1720 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1721 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 1722 | }, 1723 | "mime-db": { 1724 | "version": "1.40.0", 1725 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 1726 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" 1727 | }, 1728 | "mime-types": { 1729 | "version": "2.1.24", 1730 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 1731 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 1732 | "requires": { 1733 | "mime-db": "1.40.0" 1734 | } 1735 | }, 1736 | "minimatch": { 1737 | "version": "3.0.4", 1738 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1739 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1740 | "requires": { 1741 | "brace-expansion": "^1.1.7" 1742 | } 1743 | }, 1744 | "minimist": { 1745 | "version": "0.0.8", 1746 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1747 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 1748 | }, 1749 | "mkdirp": { 1750 | "version": "0.5.1", 1751 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1752 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1753 | "requires": { 1754 | "minimist": "0.0.8" 1755 | } 1756 | }, 1757 | "mocha": { 1758 | "version": "5.2.0", 1759 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 1760 | "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", 1761 | "dev": true, 1762 | "requires": { 1763 | "browser-stdout": "1.3.1", 1764 | "commander": "2.15.1", 1765 | "debug": "3.1.0", 1766 | "diff": "3.5.0", 1767 | "escape-string-regexp": "1.0.5", 1768 | "glob": "7.1.2", 1769 | "growl": "1.10.5", 1770 | "he": "1.1.1", 1771 | "minimatch": "3.0.4", 1772 | "mkdirp": "0.5.1", 1773 | "supports-color": "5.4.0" 1774 | }, 1775 | "dependencies": { 1776 | "debug": { 1777 | "version": "3.1.0", 1778 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 1779 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 1780 | "dev": true, 1781 | "requires": { 1782 | "ms": "2.0.0" 1783 | } 1784 | }, 1785 | "glob": { 1786 | "version": "7.1.2", 1787 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 1788 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 1789 | "dev": true, 1790 | "requires": { 1791 | "fs.realpath": "^1.0.0", 1792 | "inflight": "^1.0.4", 1793 | "inherits": "2", 1794 | "minimatch": "^3.0.4", 1795 | "once": "^1.3.0", 1796 | "path-is-absolute": "^1.0.0" 1797 | } 1798 | }, 1799 | "supports-color": { 1800 | "version": "5.4.0", 1801 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 1802 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 1803 | "dev": true, 1804 | "requires": { 1805 | "has-flag": "^3.0.0" 1806 | } 1807 | } 1808 | } 1809 | }, 1810 | "moment": { 1811 | "version": "2.24.0", 1812 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", 1813 | "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", 1814 | "dev": true 1815 | }, 1816 | "mri": { 1817 | "version": "1.1.4", 1818 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", 1819 | "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", 1820 | "dev": true 1821 | }, 1822 | "ms": { 1823 | "version": "2.0.0", 1824 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1825 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1826 | }, 1827 | "mz": { 1828 | "version": "2.7.0", 1829 | "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", 1830 | "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", 1831 | "requires": { 1832 | "any-promise": "^1.0.0", 1833 | "object-assign": "^4.0.1", 1834 | "thenify-all": "^1.0.0" 1835 | } 1836 | }, 1837 | "negotiator": { 1838 | "version": "0.6.2", 1839 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 1840 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 1841 | }, 1842 | "neo-async": { 1843 | "version": "2.6.1", 1844 | "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", 1845 | "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", 1846 | "dev": true 1847 | }, 1848 | "nested-error-stacks": { 1849 | "version": "2.1.0", 1850 | "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", 1851 | "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", 1852 | "dev": true 1853 | }, 1854 | "nice-try": { 1855 | "version": "1.0.5", 1856 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 1857 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 1858 | "dev": true 1859 | }, 1860 | "nise": { 1861 | "version": "1.5.1", 1862 | "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.1.tgz", 1863 | "integrity": "sha512-edFWm0fsFG2n318rfEnKlTZTkjlbVOFF9XIA+fj+Ed+Qz1laYW2lobwavWoMzGrYDHH1EpiNJgDfvGnkZztR/g==", 1864 | "dev": true, 1865 | "requires": { 1866 | "@sinonjs/formatio": "^3.2.1", 1867 | "@sinonjs/text-encoding": "^0.7.1", 1868 | "just-extend": "^4.0.2", 1869 | "lolex": "^4.1.0", 1870 | "path-to-regexp": "^1.7.0" 1871 | }, 1872 | "dependencies": { 1873 | "isarray": { 1874 | "version": "0.0.1", 1875 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1876 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 1877 | "dev": true 1878 | }, 1879 | "path-to-regexp": { 1880 | "version": "1.7.0", 1881 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", 1882 | "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", 1883 | "dev": true, 1884 | "requires": { 1885 | "isarray": "0.0.1" 1886 | } 1887 | } 1888 | } 1889 | }, 1890 | "normalize-package-data": { 1891 | "version": "2.5.0", 1892 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 1893 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 1894 | "dev": true, 1895 | "requires": { 1896 | "hosted-git-info": "^2.1.4", 1897 | "resolve": "^1.10.0", 1898 | "semver": "2 || 3 || 4 || 5", 1899 | "validate-npm-package-license": "^3.0.1" 1900 | } 1901 | }, 1902 | "nyc": { 1903 | "version": "14.1.1", 1904 | "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", 1905 | "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", 1906 | "dev": true, 1907 | "requires": { 1908 | "archy": "^1.0.0", 1909 | "caching-transform": "^3.0.2", 1910 | "convert-source-map": "^1.6.0", 1911 | "cp-file": "^6.2.0", 1912 | "find-cache-dir": "^2.1.0", 1913 | "find-up": "^3.0.0", 1914 | "foreground-child": "^1.5.6", 1915 | "glob": "^7.1.3", 1916 | "istanbul-lib-coverage": "^2.0.5", 1917 | "istanbul-lib-hook": "^2.0.7", 1918 | "istanbul-lib-instrument": "^3.3.0", 1919 | "istanbul-lib-report": "^2.0.8", 1920 | "istanbul-lib-source-maps": "^3.0.6", 1921 | "istanbul-reports": "^2.2.4", 1922 | "js-yaml": "^3.13.1", 1923 | "make-dir": "^2.1.0", 1924 | "merge-source-map": "^1.1.0", 1925 | "resolve-from": "^4.0.0", 1926 | "rimraf": "^2.6.3", 1927 | "signal-exit": "^3.0.2", 1928 | "spawn-wrap": "^1.4.2", 1929 | "test-exclude": "^5.2.3", 1930 | "uuid": "^3.3.2", 1931 | "yargs": "^13.2.2", 1932 | "yargs-parser": "^13.0.0" 1933 | } 1934 | }, 1935 | "object-assign": { 1936 | "version": "4.1.1", 1937 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1938 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 1939 | }, 1940 | "on-finished": { 1941 | "version": "2.3.0", 1942 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1943 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1944 | "requires": { 1945 | "ee-first": "1.1.1" 1946 | } 1947 | }, 1948 | "once": { 1949 | "version": "1.4.0", 1950 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1951 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1952 | "requires": { 1953 | "wrappy": "1" 1954 | } 1955 | }, 1956 | "openapi3-ts": { 1957 | "version": "1.3.0", 1958 | "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-1.3.0.tgz", 1959 | "integrity": "sha512-Xk3hsB0PzB4dzr/r/FdmK+VfQbZH7lQQ2iipMS1/1eoz1wUvh5R7rmOakYvw0bQJJE6PYrOLx8UHsYmzgTr+YQ==" 1960 | }, 1961 | "optimist": { 1962 | "version": "0.6.1", 1963 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 1964 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 1965 | "dev": true, 1966 | "requires": { 1967 | "minimist": "~0.0.1", 1968 | "wordwrap": "~0.0.2" 1969 | } 1970 | }, 1971 | "os-homedir": { 1972 | "version": "1.0.2", 1973 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 1974 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", 1975 | "dev": true 1976 | }, 1977 | "p-limit": { 1978 | "version": "2.2.1", 1979 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", 1980 | "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", 1981 | "requires": { 1982 | "p-try": "^2.0.0" 1983 | } 1984 | }, 1985 | "p-locate": { 1986 | "version": "3.0.0", 1987 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 1988 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 1989 | "requires": { 1990 | "p-limit": "^2.0.0" 1991 | } 1992 | }, 1993 | "p-try": { 1994 | "version": "2.2.0", 1995 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1996 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" 1997 | }, 1998 | "package-hash": { 1999 | "version": "3.0.0", 2000 | "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", 2001 | "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", 2002 | "dev": true, 2003 | "requires": { 2004 | "graceful-fs": "^4.1.15", 2005 | "hasha": "^3.0.0", 2006 | "lodash.flattendeep": "^4.4.0", 2007 | "release-zalgo": "^1.0.0" 2008 | } 2009 | }, 2010 | "packet-reader": { 2011 | "version": "1.0.0", 2012 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", 2013 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" 2014 | }, 2015 | "parent-require": { 2016 | "version": "1.0.0", 2017 | "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", 2018 | "integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=" 2019 | }, 2020 | "parse-json": { 2021 | "version": "4.0.0", 2022 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", 2023 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", 2024 | "dev": true, 2025 | "requires": { 2026 | "error-ex": "^1.3.1", 2027 | "json-parse-better-errors": "^1.0.1" 2028 | } 2029 | }, 2030 | "parse5": { 2031 | "version": "4.0.0", 2032 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", 2033 | "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" 2034 | }, 2035 | "parseurl": { 2036 | "version": "1.3.3", 2037 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 2038 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 2039 | }, 2040 | "path-exists": { 2041 | "version": "3.0.0", 2042 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 2043 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" 2044 | }, 2045 | "path-is-absolute": { 2046 | "version": "1.0.1", 2047 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 2048 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 2049 | }, 2050 | "path-key": { 2051 | "version": "2.0.1", 2052 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 2053 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 2054 | "dev": true 2055 | }, 2056 | "path-parse": { 2057 | "version": "1.0.6", 2058 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 2059 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 2060 | "dev": true 2061 | }, 2062 | "path-to-regexp": { 2063 | "version": "0.1.7", 2064 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 2065 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 2066 | }, 2067 | "path-type": { 2068 | "version": "3.0.0", 2069 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", 2070 | "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", 2071 | "dev": true, 2072 | "requires": { 2073 | "pify": "^3.0.0" 2074 | }, 2075 | "dependencies": { 2076 | "pify": { 2077 | "version": "3.0.0", 2078 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 2079 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 2080 | "dev": true 2081 | } 2082 | } 2083 | }, 2084 | "pathval": { 2085 | "version": "1.1.0", 2086 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 2087 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 2088 | "dev": true 2089 | }, 2090 | "pg": { 2091 | "version": "7.12.1", 2092 | "resolved": "https://registry.npmjs.org/pg/-/pg-7.12.1.tgz", 2093 | "integrity": "sha512-l1UuyfEvoswYfcUe6k+JaxiN+5vkOgYcVSbSuw3FvdLqDbaoa2RJo1zfJKfPsSYPFVERd4GHvX3s2PjG1asSDA==", 2094 | "requires": { 2095 | "buffer-writer": "2.0.0", 2096 | "packet-reader": "1.0.0", 2097 | "pg-connection-string": "0.1.3", 2098 | "pg-pool": "^2.0.4", 2099 | "pg-types": "^2.1.0", 2100 | "pgpass": "1.x", 2101 | "semver": "4.3.2" 2102 | } 2103 | }, 2104 | "pg-connection-string": { 2105 | "version": "0.1.3", 2106 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", 2107 | "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=" 2108 | }, 2109 | "pg-int8": { 2110 | "version": "1.0.1", 2111 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", 2112 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" 2113 | }, 2114 | "pg-pool": { 2115 | "version": "2.0.7", 2116 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.7.tgz", 2117 | "integrity": "sha512-UiJyO5B9zZpu32GSlP0tXy8J2NsJ9EFGFfz5v6PSbdz/1hBLX1rNiiy5+mAm5iJJYwfCv4A0EBcQLGWwjbpzZw==" 2118 | }, 2119 | "pg-types": { 2120 | "version": "2.2.0", 2121 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", 2122 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", 2123 | "requires": { 2124 | "pg-int8": "1.0.1", 2125 | "postgres-array": "~2.0.0", 2126 | "postgres-bytea": "~1.0.0", 2127 | "postgres-date": "~1.0.4", 2128 | "postgres-interval": "^1.1.0" 2129 | } 2130 | }, 2131 | "pgpass": { 2132 | "version": "1.0.2", 2133 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", 2134 | "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", 2135 | "requires": { 2136 | "split": "^1.0.0" 2137 | } 2138 | }, 2139 | "pify": { 2140 | "version": "4.0.1", 2141 | "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", 2142 | "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", 2143 | "dev": true 2144 | }, 2145 | "pino": { 2146 | "version": "5.13.2", 2147 | "resolved": "https://registry.npmjs.org/pino/-/pino-5.13.2.tgz", 2148 | "integrity": "sha512-WwOSCy36/gWhinsqWqAnuwIi2WtcH+jvoyeLm3bjUALIrzWIst0AovQjK4jVvSN2l64KFPfi3gd2fjsTovjdLQ==", 2149 | "requires": { 2150 | "fast-redact": "^1.4.4", 2151 | "fast-safe-stringify": "^2.0.6", 2152 | "flatstr": "^1.0.9", 2153 | "pino-std-serializers": "^2.3.0", 2154 | "quick-format-unescaped": "^3.0.2", 2155 | "sonic-boom": "^0.7.5" 2156 | } 2157 | }, 2158 | "pino-http": { 2159 | "version": "4.2.0", 2160 | "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-4.2.0.tgz", 2161 | "integrity": "sha512-yLOpH8fwnUJ3n++QmjS9HtxooJN8OKKcbbW+deRh7GqNHyY5+M9ehmH1X69pY+vuCxBY6hKGJGO2wmHG6OEmDQ==", 2162 | "requires": { 2163 | "pino": "^5.0.0", 2164 | "pino-std-serializers": "^2.4.0" 2165 | } 2166 | }, 2167 | "pino-pretty": { 2168 | "version": "2.6.1", 2169 | "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-2.6.1.tgz", 2170 | "integrity": "sha512-e/CWtKLidqkr7sinfIVVcsfcHgnFVlGvuEfKuuPFnxBo+9dZZsmgF8a9Rj7SYJ5LMZ8YBxNY9Ca46eam4ajKtQ==", 2171 | "dev": true, 2172 | "requires": { 2173 | "args": "^5.0.0", 2174 | "chalk": "^2.3.2", 2175 | "dateformat": "^3.0.3", 2176 | "fast-json-parse": "^1.0.3", 2177 | "fast-safe-stringify": "^2.0.6", 2178 | "jmespath": "^0.15.0", 2179 | "pump": "^3.0.0", 2180 | "readable-stream": "^3.0.6", 2181 | "split2": "^3.0.0" 2182 | }, 2183 | "dependencies": { 2184 | "pump": { 2185 | "version": "3.0.0", 2186 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 2187 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 2188 | "dev": true, 2189 | "requires": { 2190 | "end-of-stream": "^1.1.0", 2191 | "once": "^1.3.1" 2192 | } 2193 | }, 2194 | "readable-stream": { 2195 | "version": "3.4.0", 2196 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", 2197 | "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", 2198 | "dev": true, 2199 | "requires": { 2200 | "inherits": "^2.0.3", 2201 | "string_decoder": "^1.1.1", 2202 | "util-deprecate": "^1.0.1" 2203 | } 2204 | } 2205 | } 2206 | }, 2207 | "pino-std-serializers": { 2208 | "version": "2.4.2", 2209 | "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.4.2.tgz", 2210 | "integrity": "sha512-WaL504dO8eGs+vrK+j4BuQQq6GLKeCCcHaMB2ItygzVURcL1CycwNEUHTD/lHFHs/NL5qAz2UKrjYWXKSf4aMQ==" 2211 | }, 2212 | "pkg-dir": { 2213 | "version": "3.0.0", 2214 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", 2215 | "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", 2216 | "dev": true, 2217 | "requires": { 2218 | "find-up": "^3.0.0" 2219 | } 2220 | }, 2221 | "postgres-array": { 2222 | "version": "2.0.0", 2223 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", 2224 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" 2225 | }, 2226 | "postgres-bytea": { 2227 | "version": "1.0.0", 2228 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 2229 | "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" 2230 | }, 2231 | "postgres-date": { 2232 | "version": "1.0.4", 2233 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.4.tgz", 2234 | "integrity": "sha512-bESRvKVuTrjoBluEcpv2346+6kgB7UlnqWZsnbnCccTNq/pqfj1j6oBaN5+b/NrDXepYUT/HKadqv3iS9lJuVA==" 2235 | }, 2236 | "postgres-interval": { 2237 | "version": "1.2.0", 2238 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", 2239 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", 2240 | "requires": { 2241 | "xtend": "^4.0.0" 2242 | } 2243 | }, 2244 | "process-nextick-args": { 2245 | "version": "2.0.1", 2246 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 2247 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 2248 | "dev": true 2249 | }, 2250 | "proxy-addr": { 2251 | "version": "2.0.5", 2252 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 2253 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 2254 | "requires": { 2255 | "forwarded": "~0.1.2", 2256 | "ipaddr.js": "1.9.0" 2257 | } 2258 | }, 2259 | "pseudomap": { 2260 | "version": "1.0.2", 2261 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 2262 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 2263 | "dev": true 2264 | }, 2265 | "psl": { 2266 | "version": "1.3.0", 2267 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.3.0.tgz", 2268 | "integrity": "sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag==" 2269 | }, 2270 | "pump": { 2271 | "version": "1.0.3", 2272 | "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", 2273 | "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", 2274 | "dev": true, 2275 | "requires": { 2276 | "end-of-stream": "^1.1.0", 2277 | "once": "^1.3.1" 2278 | } 2279 | }, 2280 | "punycode": { 2281 | "version": "2.1.1", 2282 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 2283 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 2284 | }, 2285 | "qs": { 2286 | "version": "6.7.0", 2287 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 2288 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 2289 | }, 2290 | "quick-format-unescaped": { 2291 | "version": "3.0.2", 2292 | "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-3.0.2.tgz", 2293 | "integrity": "sha512-FXTaCkwvpIlkdKeGDNgcq07SXWS383noQUuZjvdE1QcTt+eLuqof6/BDiEPqB59FWLie/l91+HtlJSw7iCViSA==" 2294 | }, 2295 | "ramda": { 2296 | "version": "0.26.1", 2297 | "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", 2298 | "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==" 2299 | }, 2300 | "range-parser": { 2301 | "version": "1.2.1", 2302 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 2303 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 2304 | }, 2305 | "raw-body": { 2306 | "version": "2.4.0", 2307 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 2308 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 2309 | "requires": { 2310 | "bytes": "3.1.0", 2311 | "http-errors": "1.7.2", 2312 | "iconv-lite": "0.4.24", 2313 | "unpipe": "1.0.0" 2314 | } 2315 | }, 2316 | "read-pkg": { 2317 | "version": "3.0.0", 2318 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", 2319 | "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", 2320 | "dev": true, 2321 | "requires": { 2322 | "load-json-file": "^4.0.0", 2323 | "normalize-package-data": "^2.3.2", 2324 | "path-type": "^3.0.0" 2325 | } 2326 | }, 2327 | "read-pkg-up": { 2328 | "version": "4.0.0", 2329 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", 2330 | "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", 2331 | "dev": true, 2332 | "requires": { 2333 | "find-up": "^3.0.0", 2334 | "read-pkg": "^3.0.0" 2335 | } 2336 | }, 2337 | "readable-stream": { 2338 | "version": "2.3.6", 2339 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 2340 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 2341 | "dev": true, 2342 | "requires": { 2343 | "core-util-is": "~1.0.0", 2344 | "inherits": "~2.0.3", 2345 | "isarray": "~1.0.0", 2346 | "process-nextick-args": "~2.0.0", 2347 | "safe-buffer": "~5.1.1", 2348 | "string_decoder": "~1.1.1", 2349 | "util-deprecate": "~1.0.1" 2350 | } 2351 | }, 2352 | "reflect-metadata": { 2353 | "version": "0.1.13", 2354 | "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", 2355 | "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" 2356 | }, 2357 | "release-zalgo": { 2358 | "version": "1.0.0", 2359 | "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", 2360 | "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", 2361 | "dev": true, 2362 | "requires": { 2363 | "es6-error": "^4.0.1" 2364 | } 2365 | }, 2366 | "request-promise-core": { 2367 | "version": "1.1.2", 2368 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", 2369 | "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", 2370 | "requires": { 2371 | "lodash": "^4.17.11" 2372 | } 2373 | }, 2374 | "request-promise-native": { 2375 | "version": "1.0.7", 2376 | "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", 2377 | "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", 2378 | "requires": { 2379 | "request-promise-core": "1.1.2", 2380 | "stealthy-require": "^1.1.1", 2381 | "tough-cookie": "^2.3.3" 2382 | } 2383 | }, 2384 | "require-directory": { 2385 | "version": "2.1.1", 2386 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 2387 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 2388 | }, 2389 | "require-main-filename": { 2390 | "version": "2.0.0", 2391 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 2392 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" 2393 | }, 2394 | "resolve": { 2395 | "version": "1.12.0", 2396 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", 2397 | "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", 2398 | "dev": true, 2399 | "requires": { 2400 | "path-parse": "^1.0.6" 2401 | } 2402 | }, 2403 | "resolve-from": { 2404 | "version": "4.0.0", 2405 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 2406 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 2407 | "dev": true 2408 | }, 2409 | "rimraf": { 2410 | "version": "2.7.1", 2411 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 2412 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 2413 | "dev": true, 2414 | "requires": { 2415 | "glob": "^7.1.3" 2416 | } 2417 | }, 2418 | "routing-controllers": { 2419 | "version": "0.7.7", 2420 | "resolved": "https://registry.npmjs.org/routing-controllers/-/routing-controllers-0.7.7.tgz", 2421 | "integrity": "sha512-Z2173DC3dqDvnTxwFPx9mToX5DZ0MsgMae7ENwuK45dbJPK3D8vdZAzSgTcFR+CtrzfDR66Y8E5I3CYkPJWVWg==", 2422 | "requires": { 2423 | "class-transformer": "^0.1.9", 2424 | "class-validator": "^0.8.1", 2425 | "cookie": "^0.3.1", 2426 | "glob": "^7.0.5", 2427 | "reflect-metadata": "^0.1.12", 2428 | "template-url": "^1.0.0" 2429 | }, 2430 | "dependencies": { 2431 | "class-validator": { 2432 | "version": "0.8.5", 2433 | "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.8.5.tgz", 2434 | "integrity": "sha512-84yezRo44aP4oGhvPmqj6obAFQF1NzUyfR0+f8jubzdAspO5pmjpHhBBlPf335epUskzXAFe5uo4Qf+c7SI+DA==", 2435 | "requires": { 2436 | "validator": "9.2.0" 2437 | } 2438 | }, 2439 | "cookie": { 2440 | "version": "0.3.1", 2441 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 2442 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 2443 | }, 2444 | "validator": { 2445 | "version": "9.2.0", 2446 | "resolved": "https://registry.npmjs.org/validator/-/validator-9.2.0.tgz", 2447 | "integrity": "sha512-6Ij4Eo0KM4LkR0d0IegOwluG5453uqT5QyF5SV5Ezvm8/zmkKI/L4eoraafZGlZPC9guLkwKzgypcw8VGWWnGA==" 2448 | } 2449 | } 2450 | }, 2451 | "safe-buffer": { 2452 | "version": "5.1.2", 2453 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 2454 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 2455 | }, 2456 | "safer-buffer": { 2457 | "version": "2.1.2", 2458 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 2459 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 2460 | }, 2461 | "sax": { 2462 | "version": "1.2.4", 2463 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 2464 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 2465 | }, 2466 | "semver": { 2467 | "version": "4.3.2", 2468 | "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", 2469 | "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" 2470 | }, 2471 | "send": { 2472 | "version": "0.17.1", 2473 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 2474 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 2475 | "requires": { 2476 | "debug": "2.6.9", 2477 | "depd": "~1.1.2", 2478 | "destroy": "~1.0.4", 2479 | "encodeurl": "~1.0.2", 2480 | "escape-html": "~1.0.3", 2481 | "etag": "~1.8.1", 2482 | "fresh": "0.5.2", 2483 | "http-errors": "~1.7.2", 2484 | "mime": "1.6.0", 2485 | "ms": "2.1.1", 2486 | "on-finished": "~2.3.0", 2487 | "range-parser": "~1.2.1", 2488 | "statuses": "~1.5.0" 2489 | }, 2490 | "dependencies": { 2491 | "ms": { 2492 | "version": "2.1.1", 2493 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 2494 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 2495 | } 2496 | } 2497 | }, 2498 | "serve-static": { 2499 | "version": "1.14.1", 2500 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 2501 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 2502 | "requires": { 2503 | "encodeurl": "~1.0.2", 2504 | "escape-html": "~1.0.3", 2505 | "parseurl": "~1.3.3", 2506 | "send": "0.17.1" 2507 | } 2508 | }, 2509 | "set-blocking": { 2510 | "version": "2.0.0", 2511 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 2512 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 2513 | }, 2514 | "setprototypeof": { 2515 | "version": "1.1.1", 2516 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 2517 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 2518 | }, 2519 | "shebang-command": { 2520 | "version": "1.2.0", 2521 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 2522 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 2523 | "dev": true, 2524 | "requires": { 2525 | "shebang-regex": "^1.0.0" 2526 | } 2527 | }, 2528 | "shebang-regex": { 2529 | "version": "1.0.0", 2530 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 2531 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 2532 | "dev": true 2533 | }, 2534 | "signal-exit": { 2535 | "version": "3.0.2", 2536 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 2537 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 2538 | "dev": true 2539 | }, 2540 | "sinon": { 2541 | "version": "7.4.1", 2542 | "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.4.1.tgz", 2543 | "integrity": "sha512-7s9buHGHN/jqoy/v4bJgmt0m1XEkCEd/tqdHXumpBp0JSujaT4Ng84JU5wDdK4E85ZMq78NuDe0I3NAqXY8TFg==", 2544 | "dev": true, 2545 | "requires": { 2546 | "@sinonjs/commons": "^1.4.0", 2547 | "@sinonjs/formatio": "^3.2.1", 2548 | "@sinonjs/samsam": "^3.3.2", 2549 | "diff": "^3.5.0", 2550 | "lolex": "^4.2.0", 2551 | "nise": "^1.5.1", 2552 | "supports-color": "^5.5.0" 2553 | } 2554 | }, 2555 | "sonic-boom": { 2556 | "version": "0.7.5", 2557 | "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-0.7.5.tgz", 2558 | "integrity": "sha512-1pKrnAV6RfvntPnarY71tpthFTM3pWZWWQdghZY8ARjtDPGzG/inxqSuRwQY/7V1woUjfyxPb437zn4p5phgnQ==", 2559 | "requires": { 2560 | "flatstr": "^1.0.12" 2561 | } 2562 | }, 2563 | "source-map": { 2564 | "version": "0.5.7", 2565 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 2566 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 2567 | "dev": true 2568 | }, 2569 | "source-map-support": { 2570 | "version": "0.5.13", 2571 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", 2572 | "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", 2573 | "dev": true, 2574 | "requires": { 2575 | "buffer-from": "^1.0.0", 2576 | "source-map": "^0.6.0" 2577 | }, 2578 | "dependencies": { 2579 | "source-map": { 2580 | "version": "0.6.1", 2581 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2582 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 2583 | "dev": true 2584 | } 2585 | } 2586 | }, 2587 | "spawn-wrap": { 2588 | "version": "1.4.3", 2589 | "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", 2590 | "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", 2591 | "dev": true, 2592 | "requires": { 2593 | "foreground-child": "^1.5.6", 2594 | "mkdirp": "^0.5.0", 2595 | "os-homedir": "^1.0.1", 2596 | "rimraf": "^2.6.2", 2597 | "signal-exit": "^3.0.2", 2598 | "which": "^1.3.0" 2599 | } 2600 | }, 2601 | "spdx-correct": { 2602 | "version": "3.1.0", 2603 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", 2604 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", 2605 | "dev": true, 2606 | "requires": { 2607 | "spdx-expression-parse": "^3.0.0", 2608 | "spdx-license-ids": "^3.0.0" 2609 | } 2610 | }, 2611 | "spdx-exceptions": { 2612 | "version": "2.2.0", 2613 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 2614 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", 2615 | "dev": true 2616 | }, 2617 | "spdx-expression-parse": { 2618 | "version": "3.0.0", 2619 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 2620 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 2621 | "dev": true, 2622 | "requires": { 2623 | "spdx-exceptions": "^2.1.0", 2624 | "spdx-license-ids": "^3.0.0" 2625 | } 2626 | }, 2627 | "spdx-license-ids": { 2628 | "version": "3.0.5", 2629 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", 2630 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", 2631 | "dev": true 2632 | }, 2633 | "split": { 2634 | "version": "1.0.1", 2635 | "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", 2636 | "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", 2637 | "requires": { 2638 | "through": "2" 2639 | } 2640 | }, 2641 | "split-ca": { 2642 | "version": "1.0.1", 2643 | "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", 2644 | "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=", 2645 | "dev": true 2646 | }, 2647 | "split2": { 2648 | "version": "3.1.1", 2649 | "resolved": "https://registry.npmjs.org/split2/-/split2-3.1.1.tgz", 2650 | "integrity": "sha512-emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q==", 2651 | "dev": true, 2652 | "requires": { 2653 | "readable-stream": "^3.0.0" 2654 | }, 2655 | "dependencies": { 2656 | "readable-stream": { 2657 | "version": "3.4.0", 2658 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", 2659 | "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", 2660 | "dev": true, 2661 | "requires": { 2662 | "inherits": "^2.0.3", 2663 | "string_decoder": "^1.1.1", 2664 | "util-deprecate": "^1.0.1" 2665 | } 2666 | } 2667 | } 2668 | }, 2669 | "sprintf-js": { 2670 | "version": "1.0.3", 2671 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 2672 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 2673 | }, 2674 | "statuses": { 2675 | "version": "1.5.0", 2676 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 2677 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 2678 | }, 2679 | "stealthy-require": { 2680 | "version": "1.1.1", 2681 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", 2682 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" 2683 | }, 2684 | "string-width": { 2685 | "version": "3.1.0", 2686 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 2687 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 2688 | "requires": { 2689 | "emoji-regex": "^7.0.1", 2690 | "is-fullwidth-code-point": "^2.0.0", 2691 | "strip-ansi": "^5.1.0" 2692 | } 2693 | }, 2694 | "string_decoder": { 2695 | "version": "1.1.1", 2696 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 2697 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 2698 | "dev": true, 2699 | "requires": { 2700 | "safe-buffer": "~5.1.0" 2701 | } 2702 | }, 2703 | "strip-ansi": { 2704 | "version": "5.2.0", 2705 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 2706 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 2707 | "requires": { 2708 | "ansi-regex": "^4.1.0" 2709 | } 2710 | }, 2711 | "strip-bom": { 2712 | "version": "3.0.0", 2713 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 2714 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", 2715 | "dev": true 2716 | }, 2717 | "superagent": { 2718 | "version": "3.8.3", 2719 | "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", 2720 | "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", 2721 | "dev": true, 2722 | "requires": { 2723 | "component-emitter": "^1.2.0", 2724 | "cookiejar": "^2.1.0", 2725 | "debug": "^3.1.0", 2726 | "extend": "^3.0.0", 2727 | "form-data": "^2.3.1", 2728 | "formidable": "^1.2.0", 2729 | "methods": "^1.1.1", 2730 | "mime": "^1.4.1", 2731 | "qs": "^6.5.1", 2732 | "readable-stream": "^2.3.5" 2733 | }, 2734 | "dependencies": { 2735 | "debug": { 2736 | "version": "3.2.6", 2737 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 2738 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 2739 | "dev": true, 2740 | "requires": { 2741 | "ms": "^2.1.1" 2742 | } 2743 | }, 2744 | "ms": { 2745 | "version": "2.1.2", 2746 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 2747 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 2748 | "dev": true 2749 | } 2750 | } 2751 | }, 2752 | "supports-color": { 2753 | "version": "5.5.0", 2754 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 2755 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 2756 | "requires": { 2757 | "has-flag": "^3.0.0" 2758 | } 2759 | }, 2760 | "tar-fs": { 2761 | "version": "1.16.3", 2762 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", 2763 | "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", 2764 | "dev": true, 2765 | "requires": { 2766 | "chownr": "^1.0.1", 2767 | "mkdirp": "^0.5.1", 2768 | "pump": "^1.0.0", 2769 | "tar-stream": "^1.1.2" 2770 | } 2771 | }, 2772 | "tar-stream": { 2773 | "version": "1.6.2", 2774 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", 2775 | "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", 2776 | "dev": true, 2777 | "requires": { 2778 | "bl": "^1.0.0", 2779 | "buffer-alloc": "^1.2.0", 2780 | "end-of-stream": "^1.0.0", 2781 | "fs-constants": "^1.0.0", 2782 | "readable-stream": "^2.3.0", 2783 | "to-buffer": "^1.1.1", 2784 | "xtend": "^4.0.0" 2785 | } 2786 | }, 2787 | "template-url": { 2788 | "version": "1.0.0", 2789 | "resolved": "https://registry.npmjs.org/template-url/-/template-url-1.0.0.tgz", 2790 | "integrity": "sha1-2UVr7nDKxmF7Rip7CNsp+4E6Cwk=" 2791 | }, 2792 | "test-exclude": { 2793 | "version": "5.2.3", 2794 | "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", 2795 | "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", 2796 | "dev": true, 2797 | "requires": { 2798 | "glob": "^7.1.3", 2799 | "minimatch": "^3.0.4", 2800 | "read-pkg-up": "^4.0.0", 2801 | "require-main-filename": "^2.0.0" 2802 | } 2803 | }, 2804 | "thenify": { 2805 | "version": "3.3.0", 2806 | "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", 2807 | "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", 2808 | "requires": { 2809 | "any-promise": "^1.0.0" 2810 | } 2811 | }, 2812 | "thenify-all": { 2813 | "version": "1.6.0", 2814 | "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", 2815 | "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", 2816 | "requires": { 2817 | "thenify": ">= 3.1.0 < 4" 2818 | } 2819 | }, 2820 | "through": { 2821 | "version": "2.3.8", 2822 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 2823 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 2824 | }, 2825 | "to-buffer": { 2826 | "version": "1.1.1", 2827 | "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", 2828 | "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", 2829 | "dev": true 2830 | }, 2831 | "to-fast-properties": { 2832 | "version": "2.0.0", 2833 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 2834 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", 2835 | "dev": true 2836 | }, 2837 | "toidentifier": { 2838 | "version": "1.0.0", 2839 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 2840 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 2841 | }, 2842 | "tough-cookie": { 2843 | "version": "2.5.0", 2844 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", 2845 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", 2846 | "requires": { 2847 | "psl": "^1.1.28", 2848 | "punycode": "^2.1.1" 2849 | } 2850 | }, 2851 | "trim-right": { 2852 | "version": "1.0.1", 2853 | "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", 2854 | "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", 2855 | "dev": true 2856 | }, 2857 | "ts-mockito": { 2858 | "version": "2.4.2", 2859 | "resolved": "https://registry.npmjs.org/ts-mockito/-/ts-mockito-2.4.2.tgz", 2860 | "integrity": "sha512-3AqLVXxjfdwlo2eC+xrzFsc5rsPtKBBhJZAnxWmyBmgT/PC+K26RIxiT2QLKcqjcJqZnuGZkwfPMx2gN31lFnw==", 2861 | "dev": true, 2862 | "requires": { 2863 | "lodash": "^4.17.5" 2864 | } 2865 | }, 2866 | "ts-node": { 2867 | "version": "7.0.1", 2868 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", 2869 | "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", 2870 | "dev": true, 2871 | "requires": { 2872 | "arrify": "^1.0.0", 2873 | "buffer-from": "^1.1.0", 2874 | "diff": "^3.1.0", 2875 | "make-error": "^1.1.1", 2876 | "minimist": "^1.2.0", 2877 | "mkdirp": "^0.5.1", 2878 | "source-map-support": "^0.5.6", 2879 | "yn": "^2.0.0" 2880 | }, 2881 | "dependencies": { 2882 | "minimist": { 2883 | "version": "1.2.0", 2884 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 2885 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", 2886 | "dev": true 2887 | } 2888 | } 2889 | }, 2890 | "tslib": { 2891 | "version": "1.10.0", 2892 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", 2893 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" 2894 | }, 2895 | "type-detect": { 2896 | "version": "4.0.8", 2897 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 2898 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 2899 | "dev": true 2900 | }, 2901 | "type-is": { 2902 | "version": "1.6.18", 2903 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 2904 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 2905 | "requires": { 2906 | "media-typer": "0.3.0", 2907 | "mime-types": "~2.1.24" 2908 | } 2909 | }, 2910 | "typedarray": { 2911 | "version": "0.0.6", 2912 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 2913 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 2914 | "dev": true 2915 | }, 2916 | "typeorm": { 2917 | "version": "0.2.18", 2918 | "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.18.tgz", 2919 | "integrity": "sha512-S553GwtG5ab268+VmaLCN7gKDqFPIzUw0eGMTobJ9yr0Np62Ojfx8j1Oa9bIeh5p7Pz1/kmGabAHoP1MYK05pA==", 2920 | "requires": { 2921 | "app-root-path": "^2.0.1", 2922 | "buffer": "^5.1.0", 2923 | "chalk": "^2.4.2", 2924 | "cli-highlight": "^2.0.0", 2925 | "debug": "^4.1.1", 2926 | "dotenv": "^6.2.0", 2927 | "glob": "^7.1.2", 2928 | "js-yaml": "^3.13.1", 2929 | "mkdirp": "^0.5.1", 2930 | "reflect-metadata": "^0.1.13", 2931 | "tslib": "^1.9.0", 2932 | "xml2js": "^0.4.17", 2933 | "yargonaut": "^1.1.2", 2934 | "yargs": "^13.2.1" 2935 | }, 2936 | "dependencies": { 2937 | "debug": { 2938 | "version": "4.1.1", 2939 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 2940 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 2941 | "requires": { 2942 | "ms": "^2.1.1" 2943 | } 2944 | }, 2945 | "ms": { 2946 | "version": "2.1.2", 2947 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 2948 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 2949 | } 2950 | } 2951 | }, 2952 | "typescript": { 2953 | "version": "3.5.3", 2954 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", 2955 | "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", 2956 | "dev": true 2957 | }, 2958 | "uglify-js": { 2959 | "version": "3.6.0", 2960 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", 2961 | "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", 2962 | "dev": true, 2963 | "optional": true, 2964 | "requires": { 2965 | "commander": "~2.20.0", 2966 | "source-map": "~0.6.1" 2967 | }, 2968 | "dependencies": { 2969 | "commander": { 2970 | "version": "2.20.0", 2971 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", 2972 | "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", 2973 | "dev": true, 2974 | "optional": true 2975 | }, 2976 | "source-map": { 2977 | "version": "0.6.1", 2978 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2979 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 2980 | "dev": true, 2981 | "optional": true 2982 | } 2983 | } 2984 | }, 2985 | "unpipe": { 2986 | "version": "1.0.0", 2987 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 2988 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 2989 | }, 2990 | "util-deprecate": { 2991 | "version": "1.0.2", 2992 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2993 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 2994 | "dev": true 2995 | }, 2996 | "utils-merge": { 2997 | "version": "1.0.1", 2998 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 2999 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 3000 | }, 3001 | "uuid": { 3002 | "version": "3.3.3", 3003 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", 3004 | "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", 3005 | "dev": true 3006 | }, 3007 | "validate-npm-package-license": { 3008 | "version": "3.0.4", 3009 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 3010 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 3011 | "dev": true, 3012 | "requires": { 3013 | "spdx-correct": "^3.0.0", 3014 | "spdx-expression-parse": "^3.0.0" 3015 | } 3016 | }, 3017 | "validator": { 3018 | "version": "10.4.0", 3019 | "resolved": "https://registry.npmjs.org/validator/-/validator-10.4.0.tgz", 3020 | "integrity": "sha512-Q/wBy3LB1uOyssgNlXSRmaf22NxjvDNZM2MtIQ4jaEOAB61xsh1TQxsq1CgzUMBV1lDrVMogIh8GjG1DYW0zLg==" 3021 | }, 3022 | "vary": { 3023 | "version": "1.1.2", 3024 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 3025 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 3026 | }, 3027 | "which": { 3028 | "version": "1.3.1", 3029 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 3030 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 3031 | "dev": true, 3032 | "requires": { 3033 | "isexe": "^2.0.0" 3034 | } 3035 | }, 3036 | "which-module": { 3037 | "version": "2.0.0", 3038 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 3039 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" 3040 | }, 3041 | "wordwrap": { 3042 | "version": "0.0.3", 3043 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 3044 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", 3045 | "dev": true 3046 | }, 3047 | "wrap-ansi": { 3048 | "version": "5.1.0", 3049 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 3050 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 3051 | "requires": { 3052 | "ansi-styles": "^3.2.0", 3053 | "string-width": "^3.0.0", 3054 | "strip-ansi": "^5.0.0" 3055 | } 3056 | }, 3057 | "wrappy": { 3058 | "version": "1.0.2", 3059 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 3060 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 3061 | }, 3062 | "write-file-atomic": { 3063 | "version": "2.4.3", 3064 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", 3065 | "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", 3066 | "dev": true, 3067 | "requires": { 3068 | "graceful-fs": "^4.1.11", 3069 | "imurmurhash": "^0.1.4", 3070 | "signal-exit": "^3.0.2" 3071 | } 3072 | }, 3073 | "xml2js": { 3074 | "version": "0.4.19", 3075 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 3076 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 3077 | "requires": { 3078 | "sax": ">=0.6.0", 3079 | "xmlbuilder": "~9.0.1" 3080 | } 3081 | }, 3082 | "xmlbuilder": { 3083 | "version": "9.0.7", 3084 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 3085 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 3086 | }, 3087 | "xtend": { 3088 | "version": "4.0.2", 3089 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 3090 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 3091 | }, 3092 | "y18n": { 3093 | "version": "4.0.0", 3094 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 3095 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" 3096 | }, 3097 | "yallist": { 3098 | "version": "2.1.2", 3099 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 3100 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 3101 | "dev": true 3102 | }, 3103 | "yargonaut": { 3104 | "version": "1.1.4", 3105 | "resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz", 3106 | "integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==", 3107 | "requires": { 3108 | "chalk": "^1.1.1", 3109 | "figlet": "^1.1.1", 3110 | "parent-require": "^1.0.0" 3111 | }, 3112 | "dependencies": { 3113 | "ansi-regex": { 3114 | "version": "2.1.1", 3115 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 3116 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 3117 | }, 3118 | "ansi-styles": { 3119 | "version": "2.2.1", 3120 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 3121 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" 3122 | }, 3123 | "chalk": { 3124 | "version": "1.1.3", 3125 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 3126 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 3127 | "requires": { 3128 | "ansi-styles": "^2.2.1", 3129 | "escape-string-regexp": "^1.0.2", 3130 | "has-ansi": "^2.0.0", 3131 | "strip-ansi": "^3.0.0", 3132 | "supports-color": "^2.0.0" 3133 | } 3134 | }, 3135 | "strip-ansi": { 3136 | "version": "3.0.1", 3137 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 3138 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 3139 | "requires": { 3140 | "ansi-regex": "^2.0.0" 3141 | } 3142 | }, 3143 | "supports-color": { 3144 | "version": "2.0.0", 3145 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 3146 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" 3147 | } 3148 | } 3149 | }, 3150 | "yargs": { 3151 | "version": "13.3.0", 3152 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", 3153 | "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", 3154 | "requires": { 3155 | "cliui": "^5.0.0", 3156 | "find-up": "^3.0.0", 3157 | "get-caller-file": "^2.0.1", 3158 | "require-directory": "^2.1.1", 3159 | "require-main-filename": "^2.0.0", 3160 | "set-blocking": "^2.0.0", 3161 | "string-width": "^3.0.0", 3162 | "which-module": "^2.0.0", 3163 | "y18n": "^4.0.0", 3164 | "yargs-parser": "^13.1.1" 3165 | } 3166 | }, 3167 | "yargs-parser": { 3168 | "version": "13.1.1", 3169 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", 3170 | "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", 3171 | "requires": { 3172 | "camelcase": "^5.0.0", 3173 | "decamelize": "^1.2.0" 3174 | } 3175 | }, 3176 | "yn": { 3177 | "version": "2.0.0", 3178 | "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", 3179 | "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", 3180 | "dev": true 3181 | } 3182 | } 3183 | } 3184 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsfest-2019-demo-hexagonal", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "start": "node dist/src/server.js", 11 | "tslint": "tslint -c tslint.json -p tsconfig.json", 12 | "build": "tsc", 13 | "test:acceptance": "mocha dist/test/acceptance/**/*.spec.js", 14 | "test:integration": "mocha dist/test/integration/**/*.spec.js -t 30000", 15 | "test:unit": "mocha dist/test/unit/**/*.spec.js", 16 | "test": "mocha dist/test/**/*.spec.js -t 30000", 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/Stormteller/jsfest-2019-demo-hexagonal.git" 21 | }, 22 | "author": "", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/Stormteller/jsfest-2019-demo-hexagonal/issues" 26 | }, 27 | "homepage": "https://github.com/Stormteller/jsfest-2019-demo-hexagonal#readme", 28 | "dependencies": { 29 | "body-parser": "^1.18.3", 30 | "class-transformer": "^0.1.10", 31 | "class-validator": "^0.9.1", 32 | "class-validator-jsonschema": "^1.2.0", 33 | "dotenv": "^6.1.0", 34 | "express": "^4.16.4", 35 | "express-pino-logger": "^4.0.0", 36 | "glob": "^7.1.3", 37 | "inversify": "^5.0.1", 38 | "pg": "^7.7.1", 39 | "pino": "^5.9.0", 40 | "ramda": "^0.26.1", 41 | "reflect-metadata": "^0.1.12", 42 | "request-promise-native": "^1.0.7", 43 | "routing-controllers": "^0.7.7", 44 | "typeorm": "^0.2.18" 45 | }, 46 | "devDependencies": { 47 | "@types/body-parser": "^1.17.0", 48 | "@types/chai": "^4.1.7", 49 | "@types/chai-http": "^4.2.0", 50 | "@types/dockerode": "^2.5.10", 51 | "@types/dotenv": "^6.1.0", 52 | "@types/express": "^4.16.0", 53 | "@types/mocha": "^5.2.5", 54 | "@types/node": "^10.12.9", 55 | "@types/pg": "^7.4.11", 56 | "@types/pino": "^5.8.3", 57 | "@types/ramda": "^0.25.46", 58 | "@types/request-promise-native": "^1.0.15", 59 | "@types/sinon": "^5.0.7", 60 | "chai": "^4.2.0", 61 | "chai-http": "^4.2.1", 62 | "cross-env": "^5.2.0", 63 | "dockerode": "^2.5.7", 64 | "mocha": "^5.2.0", 65 | "nyc": "^14.0.0", 66 | "pino-pretty": "^2.4.0", 67 | "sinon": "^7.1.1", 68 | "ts-mockito": "^2.3.1", 69 | "ts-node": "^7.0.1", 70 | "typescript": "^3.1.6" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/AppInitilizer.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | import {Container} from 'inversify'; 3 | import * as ExpressPinoLogger from 'express-pino-logger'; 4 | import * as bodyParser from 'body-parser'; 5 | 6 | import config from './configuration/Config'; 7 | import logger from './configuration/Logger'; 8 | import { 9 | Action, 10 | RoutingControllersOptions, 11 | useContainer as useContainerRoutingController, 12 | useExpressServer 13 | } from 'routing-controllers'; 14 | import {createDIContainer} from './configuration/DIContainer'; 15 | import {createConnectionPool} from './secondaryAdapters/postgres/Connection'; 16 | 17 | class AppInitializer { 18 | public app: express.Application; 19 | public router: express.Router; 20 | public diContainer: Container; 21 | public routingControllerOptions: RoutingControllersOptions; 22 | 23 | public async initialize(): Promise { 24 | 25 | this.app = express(); 26 | 27 | this.initHttpLogging(); 28 | 29 | this.initContainer(); 30 | 31 | await this.initDatabase(); 32 | 33 | this.initMiddleware(); 34 | 35 | this.initRoutes(); 36 | 37 | return this.app; 38 | } 39 | 40 | private async initDatabase(): Promise { 41 | await createConnectionPool(); 42 | } 43 | 44 | private initMiddleware(): void { 45 | this.app.disable('x-powered-by'); 46 | this.app.use(bodyParser.urlencoded({'extended': true})); 47 | this.app.use(bodyParser.json()); 48 | } 49 | 50 | private initRoutes(): void { 51 | this.router = express.Router(); 52 | this.routingControllerOptions = { 53 | controllers: config.controllerPaths, 54 | middlewares: config.middlewarePaths, 55 | routePrefix: config.apiPrefix, 56 | defaultErrorHandler: false, 57 | validation: true, 58 | classTransformer: true, 59 | authorizationChecker: (action: Action) => action.request.get('user_id') != null, 60 | currentUserChecker: (action: Action) => Number(action.request.get('user_id')) 61 | }; 62 | useContainerRoutingController(this.diContainer); 63 | useExpressServer(this.app, this.routingControllerOptions); 64 | this.app.use(this.router); 65 | } 66 | 67 | private initContainer(): void { 68 | this.diContainer = createDIContainer(config.diContainerModulesPath); 69 | } 70 | 71 | private initHttpLogging() { 72 | this.app.use(ExpressPinoLogger({ 73 | logger: logger 74 | })); 75 | } 76 | 77 | } 78 | 79 | export default new AppInitializer(); 80 | -------------------------------------------------------------------------------- /src/configuration/Config.ts: -------------------------------------------------------------------------------- 1 | import {Level} from 'pino'; 2 | import * as DotEnv from 'dotenv'; 3 | import {DotenvConfigOptions} from 'dotenv'; 4 | import * as assert from 'assert'; 5 | 6 | export const ALLOWED_NODE_ENV = ['local', 'dev', 'staging', 'prod', 'test', 'ci']; 7 | 8 | export interface ConfigInterface { 9 | port: number; 10 | host: string; 11 | apiPrefix: string; 12 | node_env: string; 13 | logger: { 14 | level: Level; 15 | }; 16 | controllerPaths: string[]; 17 | middlewarePaths: string[]; 18 | diContainerModulesPath: string[]; 19 | } 20 | 21 | function configuration(): ConfigInterface { 22 | const nodeEnv = process.env.NODE_ENV || 'local'; 23 | assert(ALLOWED_NODE_ENV.indexOf(nodeEnv) > -1); 24 | const path = `env/.env.${nodeEnv}`; 25 | const dotEnvOptions: DotenvConfigOptions = { 26 | path: path 27 | }; 28 | DotEnv.config(dotEnvOptions); 29 | return { 30 | port: parseInt(process.env.PORT || '8080', 10), 31 | host: process.env.HOST || 'http://localhost:8080', 32 | apiPrefix: '/api', 33 | node_env: process.env.NODE_ENV || 'local', 34 | logger: { 35 | level: process.env.NODE_ENV === 'local' ? 'debug' : 'info' 36 | }, 37 | controllerPaths: [__dirname + '/../primaryAdapters/rest/**/*Controller.js'], 38 | middlewarePaths: [__dirname + '/../primaryAdapters/rest/common/HttpErrorHandlers.js'], 39 | diContainerModulesPath: [ 40 | __dirname + '/../core/component/**/*ContainerModule.js', 41 | __dirname + '/../primaryAdapters/**/*ContainerModule.js', 42 | __dirname + '/../secondaryAdapters/**/*ContainerModule.js', 43 | ], 44 | }; 45 | } 46 | 47 | const config = configuration(); 48 | 49 | export default config; 50 | -------------------------------------------------------------------------------- /src/configuration/DIContainer.ts: -------------------------------------------------------------------------------- 1 | import {Container, ContainerModule} from 'inversify'; 2 | 3 | import * as path from 'path'; 4 | import * as glob from 'glob'; 5 | 6 | export function importContainerModuleInstancesFromDirectories(directories: string[], formats = ['.js', '.ts']): ContainerModule[] { 7 | 8 | const loadFileClasses = function (exported: any, allLoaded: ContainerModule[]): ContainerModule[] { 9 | if (exported instanceof ContainerModule) { 10 | allLoaded.push(exported); 11 | } else if (exported instanceof Array) { 12 | exported.forEach((i: any) => loadFileClasses(i, allLoaded)); 13 | } else if (exported instanceof Object || typeof exported === 'object') { 14 | Object.keys(exported).forEach(key => loadFileClasses(exported[key], allLoaded)); 15 | } 16 | return allLoaded; 17 | }; 18 | 19 | const allFiles = directories.reduce( 20 | (allDirs, dir) => allDirs.concat(glob.sync(path.normalize(dir))), 21 | [] as string[] 22 | ); 23 | 24 | const dirs = allFiles 25 | .filter(file => 26 | formats.indexOf(path.extname(file)) !== -1 && 27 | file.substring(file.length - 5, file.length) !== '.d.ts' 28 | ) 29 | .map(file => require(file)); 30 | 31 | return loadFileClasses(dirs, []); 32 | } 33 | 34 | export function createDIContainer(containerModulesDirs: string[]) { 35 | let container = new Container(); 36 | 37 | const containerModules = importContainerModuleInstancesFromDirectories(containerModulesDirs); 38 | container.load(...containerModules); 39 | 40 | return container; 41 | } 42 | -------------------------------------------------------------------------------- /src/configuration/Logger.ts: -------------------------------------------------------------------------------- 1 | import config from './Config'; 2 | import * as pino from 'pino'; 3 | import {Logger, LoggerOptions} from 'pino'; 4 | 5 | function initLogger(): Logger { 6 | let loggerOptions: LoggerOptions = { 7 | level: config.logger.level 8 | }; 9 | 10 | if (config.node_env === 'local') { 11 | loggerOptions.prettyPrint = { 12 | colorize: true 13 | }; 14 | } 15 | 16 | return pino(loggerOptions); 17 | } 18 | 19 | const logger = initLogger(); 20 | 21 | export default logger; 22 | -------------------------------------------------------------------------------- /src/core/component/event/EventContainerModule.ts: -------------------------------------------------------------------------------- 1 | import {ContainerModule, interfaces} from 'inversify'; 2 | import {EventConverter} from './application/services/converters/EventConverter'; 3 | import {EventCreationInput} from './application/data/input/EventCreationInput'; 4 | import {EventCreationService} from './application/services/EventCreationService'; 5 | import {EventQueryService} from './application/services/EventQueryService'; 6 | import {EventReminderService} from './application/services/EventReminderService'; 7 | 8 | export default new ContainerModule(( 9 | bind: interfaces.Bind, 10 | unbind: interfaces.Unbind, 11 | isBound: interfaces.IsBound, 12 | rebind: interfaces.Rebind 13 | ) => { 14 | bind(EventConverter).toSelf(); 15 | bind(EventCreationService).toSelf(); 16 | bind(EventQueryService).toSelf(); 17 | bind(EventReminderService).toSelf(); 18 | }); 19 | -------------------------------------------------------------------------------- /src/core/component/event/application/data/input/EventCreationInput.ts: -------------------------------------------------------------------------------- 1 | import {EventType} from '../../../domain/data/EventType'; 2 | import {IsDate, IsDateString, IsEnum, IsNumber, IsString} from 'class-validator'; 3 | import {TransformToDate} from '../../../../../sharedKernel/annotations/transformers'; 4 | 5 | export class EventCreationInput { 6 | @IsString() 7 | title: string; 8 | 9 | @IsDate() 10 | @TransformToDate 11 | startDate: Date; 12 | 13 | @IsDate() 14 | @TransformToDate 15 | endDate: Date; 16 | 17 | @IsEnum(EventType) 18 | eventType: EventType; 19 | } 20 | -------------------------------------------------------------------------------- /src/core/component/event/application/data/output/EventResponse.ts: -------------------------------------------------------------------------------- 1 | import {EventType} from '../../../domain/data/EventType'; 2 | 3 | interface EventResponseBuilderObj { 4 | id?: number | undefined | null, 5 | title: string, 6 | startDate: Date, 7 | endDate: Date, 8 | creatorId: number, 9 | eventType: EventType, 10 | createdAt?: Date | undefined| null 11 | } 12 | 13 | export class EventResponse { 14 | id: number | null; 15 | 16 | title: string; 17 | 18 | startDate: Date; 19 | 20 | endDate: Date; 21 | 22 | creatorId: number; 23 | 24 | eventType: EventType; 25 | 26 | createdAt: Date | null; 27 | 28 | public static fromObject(builder: EventResponseBuilderObj): EventResponse { 29 | const event = new EventResponse(); 30 | event.id = builder.id || null; 31 | event.title = builder.title; 32 | event.startDate = builder.startDate; 33 | event.endDate = builder.endDate; 34 | event.creatorId = builder.creatorId; 35 | event.eventType = builder.eventType; 36 | event.createdAt = builder.createdAt || null; 37 | return event; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/core/component/event/application/services/EventCreationService.ts: -------------------------------------------------------------------------------- 1 | import {EventRepository, EventRepositoryType} from '../../port/EventRepository'; 2 | import {inject, injectable} from 'inversify'; 3 | import {EventResponse} from '../data/output/EventResponse'; 4 | import {EventCreationInput} from '../data/input/EventCreationInput'; 5 | import {EventReminderService} from './EventReminderService'; 6 | import {Event} from '../../domain/data/Event'; 7 | import {Converter} from '../../../../sharedKernel/interfaces/Converter'; 8 | import {EventConverter} from './converters/EventConverter'; 9 | 10 | @injectable() 11 | export class EventCreationService { 12 | private eventRepo: EventRepository; 13 | private reminderService: EventReminderService; 14 | private eventConverter: Converter; 15 | 16 | constructor( 17 | @inject(EventRepositoryType) eventRepo: EventRepository, 18 | @inject(EventReminderService) reminderService: EventReminderService, 19 | @inject(EventConverter) eventConverter: Converter 20 | ) { 21 | this.eventRepo = eventRepo; 22 | this.reminderService = reminderService; 23 | this.eventConverter = eventConverter; 24 | } 25 | 26 | public async createEvent(input: EventCreationInput, actionUserId: number): Promise { 27 | const newEvent = Event.fromObject({ 28 | ...input, 29 | creatorId: actionUserId 30 | }); 31 | 32 | const savedEvent = await this.eventRepo.save(newEvent); 33 | 34 | await this.reminderService.setupReminder(savedEvent); 35 | 36 | return this.eventConverter.from(savedEvent); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/core/component/event/application/services/EventQueryService.ts: -------------------------------------------------------------------------------- 1 | import {inject, injectable} from 'inversify'; 2 | import {EventRepository, EventRepositoryType} from '../../port/EventRepository'; 3 | import {EventResponse} from '../data/output/EventResponse'; 4 | import {NotFound} from '../../../../sharedKernel/exceptions/NotFound'; 5 | import {Forbidden} from '../../../../sharedKernel/exceptions/Forbidden'; 6 | import {Converter} from '../../../../sharedKernel/interfaces/Converter'; 7 | import {EventConverter} from './converters/EventConverter'; 8 | import {Event} from '../../domain/data/Event'; 9 | 10 | @injectable() 11 | export class EventQueryService { 12 | private eventRepo: EventRepository; 13 | private eventConverter: Converter; 14 | 15 | constructor( 16 | @inject(EventRepositoryType) eventRepo: EventRepository, 17 | @inject(EventConverter) eventConverter: Converter 18 | ) { 19 | this.eventRepo = eventRepo; 20 | this.eventConverter = eventConverter; 21 | } 22 | 23 | public async getEventById(eventId: number, actionUserId: number): Promise { 24 | const event = await this.eventRepo.findById(eventId); 25 | if (!event) throw new NotFound('No such event'); 26 | 27 | if (event.creatorId !== actionUserId) throw new Forbidden('Only creator can access event'); 28 | 29 | return this.eventConverter.from(event); 30 | } 31 | 32 | public async getEventsCreatedByUser(actionUserId: number): Promise { 33 | const events = await this.eventRepo.findByCreator(actionUserId); 34 | 35 | return events.map(event => this.eventConverter.from(event)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/core/component/event/application/services/EventReminderService.ts: -------------------------------------------------------------------------------- 1 | import {inject, injectable} from 'inversify'; 2 | import {NotificationService, NotificationServiceType} from '../../../../port/notification/NotificationService'; 3 | import {Event} from '../../domain/data/Event'; 4 | import {UserRepository, UserRepositoryType} from '../../../user/port/UserRepository'; 5 | import {NotFound} from '../../../../sharedKernel/exceptions/NotFound'; 6 | 7 | @injectable() 8 | export class EventReminderService { 9 | private notificationService: NotificationService; 10 | private userRepo: UserRepository; 11 | 12 | constructor( 13 | @inject(NotificationServiceType) notificationService: NotificationService, 14 | @inject(UserRepositoryType) userRepo: UserRepository 15 | ) { 16 | this.notificationService = notificationService; 17 | this.userRepo = userRepo; 18 | } 19 | 20 | public async setupReminder(event: Event): Promise { 21 | const user = await this.userRepo.findById(event.creatorId); 22 | 23 | if(!user) throw new NotFound('No such user'); 24 | 25 | setTimeout(()=> { 26 | this.sendReminder(event, user.notificationTokens) 27 | }, event.startDate.valueOf() - Date.now()) 28 | } 29 | 30 | public async sendReminder(event: Event, tokens: string[]): Promise { 31 | const message = { 32 | title: `It is time to ${event.title}`, 33 | body: 'Hello from JSFest!', 34 | data: {eventId: event.id} 35 | }; 36 | await this.notificationService.sendNotifications(message, tokens); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/core/component/event/application/services/converters/EventConverter.ts: -------------------------------------------------------------------------------- 1 | import {Converter} from '../../../../../sharedKernel/interfaces/Converter'; 2 | import {EventResponse} from '../../data/output/EventResponse'; 3 | import {injectable} from 'inversify'; 4 | import {Event} from '../../../domain/data/Event'; 5 | 6 | @injectable() 7 | export class EventConverter implements Converter { 8 | from(from: Event): EventResponse { 9 | return EventResponse.fromObject({...from}); 10 | } 11 | 12 | to(to: EventResponse): Event { 13 | return EventResponse.fromObject({...to}); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/core/component/event/domain/data/Event.ts: -------------------------------------------------------------------------------- 1 | import {EventType} from './EventType'; 2 | 3 | interface EventBuilderObj { 4 | id?: number, 5 | title: string, 6 | startDate: Date, 7 | endDate: Date, 8 | creatorId: number, 9 | eventType: EventType, 10 | createdAt?: Date 11 | } 12 | 13 | export class Event { 14 | id: number | null; 15 | 16 | title: string; 17 | 18 | startDate: Date; 19 | 20 | endDate: Date; 21 | 22 | creatorId: number; 23 | 24 | eventType: EventType; 25 | 26 | createdAt: Date | null; 27 | 28 | public static fromObject(builder: EventBuilderObj): Event { 29 | const event = new Event(); 30 | event.id = builder.id || null; 31 | event.title = builder.title; 32 | event.startDate = builder.startDate; 33 | event.endDate = builder.endDate; 34 | event.creatorId = builder.creatorId; 35 | event.eventType = builder.eventType; 36 | event.createdAt = builder.createdAt || null; 37 | return event; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/core/component/event/domain/data/EventType.ts: -------------------------------------------------------------------------------- 1 | export enum EventType { 2 | WORK = 'WORK', 3 | PERSONAL = 'PERSONAL' 4 | } 5 | -------------------------------------------------------------------------------- /src/core/component/event/port/EventRepository.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../domain/data/Event' 2 | 3 | export interface EventRepository { 4 | findById(id: number): Promise; 5 | 6 | findByCreator(creatorId: number): Promise; 7 | 8 | findByStartDateRange(dateFrom: Date, dateTo: Date): Promise; 9 | 10 | save(event: Event): Promise; 11 | } 12 | 13 | const EventRepositoryType = Symbol.for('EventRepository'); 14 | 15 | export {EventRepositoryType}; 16 | -------------------------------------------------------------------------------- /src/core/component/user/UserContainerModule.ts: -------------------------------------------------------------------------------- 1 | import {ContainerModule, interfaces} from 'inversify'; 2 | import {UserNotificationService} from './application/services/UserNotificationService'; 3 | import {UserRegistrationService} from './application/services/UserRegistrationService'; 4 | import {UserConverter} from './application/services/converters/UserConverter'; 5 | 6 | export default new ContainerModule(( 7 | bind: interfaces.Bind, 8 | unbind: interfaces.Unbind, 9 | isBound: interfaces.IsBound, 10 | rebind: interfaces.Rebind 11 | ) => { 12 | bind(UserConverter).toSelf(); 13 | bind(UserNotificationService).toSelf(); 14 | bind(UserRegistrationService).toSelf(); 15 | }); 16 | -------------------------------------------------------------------------------- /src/core/component/user/application/data/input/UserNotificationTokenInput.ts: -------------------------------------------------------------------------------- 1 | import {IsString, MaxLength} from 'class-validator'; 2 | 3 | export class UserNotificationTokenInput { 4 | @IsString() 5 | @MaxLength(255) 6 | notificationToken: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/core/component/user/application/data/input/UserRegistrationInput.ts: -------------------------------------------------------------------------------- 1 | import {IsPhoneNumber, IsString, Length} from 'class-validator'; 2 | 3 | export class UserRegistrationInput { 4 | @IsString() 5 | @Length(3, 30) 6 | username: string; 7 | 8 | @IsPhoneNumber('ZZ') 9 | phone: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/core/component/user/application/data/output/UserResponse.ts: -------------------------------------------------------------------------------- 1 | interface UserBuilderObj { 2 | id?: number | null; 3 | 4 | phone: string; 5 | 6 | username: string; 7 | } 8 | 9 | export class UserResponse { 10 | id: number | null; 11 | 12 | phone: string; 13 | 14 | username: string; 15 | 16 | public static fromObject(builder: UserBuilderObj): UserResponse { 17 | const user = new UserResponse(); 18 | user.id = builder.id || null; 19 | user.phone = builder.phone; 20 | user.username = builder.username; 21 | return user; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/core/component/user/application/services/UserNotificationService.ts: -------------------------------------------------------------------------------- 1 | import {inject, injectable} from 'inversify'; 2 | import {UserRepository, UserRepositoryType} from '../../port/UserRepository'; 3 | import {NotFound} from '../../../../sharedKernel/exceptions/NotFound'; 4 | import {Converter} from '../../../../sharedKernel/interfaces/Converter'; 5 | import {User} from '../../domain/data/User'; 6 | import {UserResponse} from '../data/output/UserResponse'; 7 | import {UserConverter} from './converters/UserConverter'; 8 | 9 | @injectable() 10 | export class UserNotificationService { 11 | private userRepo: UserRepository; 12 | private userConverter: Converter; 13 | 14 | constructor( 15 | @inject(UserRepositoryType) userRepo: UserRepository, 16 | @inject(UserConverter) userConverter: Converter 17 | ) { 18 | this.userRepo = userRepo; 19 | this.userConverter = userConverter; 20 | } 21 | 22 | public async addNotificationToken(token: string, actionUserId: number): Promise { 23 | const user = await this.userRepo.findById(actionUserId); 24 | 25 | if(!user) throw new NotFound('No such user'); 26 | 27 | const setOfTokens = new Set(user.notificationTokens); 28 | setOfTokens.add(token); 29 | user.notificationTokens = [...setOfTokens]; 30 | 31 | const savedUser = await this.userRepo.save(user); 32 | 33 | return this.userConverter.from(savedUser); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/core/component/user/application/services/UserRegistrationService.ts: -------------------------------------------------------------------------------- 1 | import {inject, injectable} from 'inversify'; 2 | import {UserRepository, UserRepositoryType} from '../../port/UserRepository'; 3 | import {Converter} from '../../../../sharedKernel/interfaces/Converter'; 4 | import {User} from '../../domain/data/User'; 5 | import {UserConverter} from './converters/UserConverter'; 6 | import {UserResponse} from '../data/output/UserResponse'; 7 | import {UserRegistrationInput} from '../data/input/UserRegistrationInput'; 8 | import {BadRequest} from '../../../../sharedKernel/exceptions/BadRequest'; 9 | 10 | @injectable() 11 | export class UserRegistrationService { 12 | private userRepo: UserRepository; 13 | private userConverter: Converter; 14 | 15 | constructor( 16 | @inject(UserRepositoryType) userRepo: UserRepository, 17 | @inject(UserConverter) userConverter: Converter 18 | ) { 19 | this.userRepo = userRepo; 20 | this.userConverter = userConverter; 21 | } 22 | 23 | public async registerUser(input: UserRegistrationInput): Promise { 24 | const userWithUsername = await this.userRepo.findByUsername(input.username); 25 | 26 | if(userWithUsername) throw new BadRequest('Username already exists'); 27 | 28 | const newUser = User.fromObject({ 29 | ...input 30 | }); 31 | 32 | const user = await this.userRepo.save(newUser); 33 | 34 | return this.userConverter.from(user); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/core/component/user/application/services/converters/UserConverter.ts: -------------------------------------------------------------------------------- 1 | import {Converter} from '../../../../../sharedKernel/interfaces/Converter'; 2 | import {UserResponse} from '../../data/output/UserResponse'; 3 | import {User} from '../../../domain/data/User'; 4 | import {injectable} from 'inversify'; 5 | 6 | @injectable() 7 | export class UserConverter implements Converter { 8 | from(from: User): UserResponse { 9 | return UserResponse.fromObject({ 10 | phone: from.phone, 11 | id: from.id, 12 | username: from.username 13 | }); 14 | } 15 | 16 | to(to: UserResponse): User { 17 | throw new Error('Cannot be implemented'); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/core/component/user/domain/data/User.ts: -------------------------------------------------------------------------------- 1 | interface UserBuilderObj { 2 | id?: number; 3 | 4 | phone: string; 5 | 6 | username: string; 7 | 8 | notificationTokens?: string[] | undefined | null; 9 | } 10 | 11 | export class User { 12 | id: number | null; 13 | 14 | phone: string; 15 | 16 | username: string; 17 | 18 | notificationTokens: string[] = []; 19 | 20 | public static fromObject(builder: UserBuilderObj): User { 21 | const user = new User(); 22 | user.id = builder.id || null; 23 | user.phone = builder.phone; 24 | user.username = builder.username; 25 | if(builder.notificationTokens != null) user.notificationTokens = builder.notificationTokens; 26 | return user; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/core/component/user/port/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import {User} from '../domain/data/User'; 2 | 3 | export interface UserRepository { 4 | findByUsername(username: string): Promise; 5 | 6 | findById(id: number): Promise; 7 | 8 | save(user: User): Promise; 9 | } 10 | 11 | const UserRepositoryType = Symbol.for('UserRepository'); 12 | 13 | export {UserRepositoryType} 14 | -------------------------------------------------------------------------------- /src/core/port/notification/NotificationService.ts: -------------------------------------------------------------------------------- 1 | import {NotificationMessage} from './data/NotificationMessage'; 2 | 3 | export interface NotificationService { 4 | sendNotifications(message: NotificationMessage, tokens: string[]): Promise; 5 | } 6 | 7 | const NotificationServiceType = Symbol.for('NotificationService'); 8 | 9 | export {NotificationServiceType}; 10 | -------------------------------------------------------------------------------- /src/core/port/notification/data/NotificationMessage.ts: -------------------------------------------------------------------------------- 1 | export interface NotificationMessage { 2 | title: string; 3 | body: string; 4 | data: any; 5 | } 6 | -------------------------------------------------------------------------------- /src/core/sharedKernel/annotations/transformers.ts: -------------------------------------------------------------------------------- 1 | import {Transform} from 'class-transformer'; 2 | 3 | export function TransformToDate(target: any, key: string) { 4 | Transform(value => value ? new Date(value) : null)(target, key); 5 | } 6 | -------------------------------------------------------------------------------- /src/core/sharedKernel/exceptions/BadRequest.ts: -------------------------------------------------------------------------------- 1 | export class BadRequest extends Error { 2 | constructor(message: string) { 3 | super(message); 4 | Object.setPrototypeOf(this, BadRequest.prototype); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/core/sharedKernel/exceptions/DuplicationError.ts: -------------------------------------------------------------------------------- 1 | import {BadRequest} from './BadRequest'; 2 | 3 | export class DuplicationError extends BadRequest { 4 | constructor(message: string = 'Already exists') { 5 | super(message); 6 | Object.setPrototypeOf(this, DuplicationError.prototype); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/core/sharedKernel/exceptions/Forbidden.ts: -------------------------------------------------------------------------------- 1 | export class Forbidden extends Error { 2 | constructor(message: string = 'Action forbidden') { 3 | super(message); 4 | Object.setPrototypeOf(this, Forbidden.prototype); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/core/sharedKernel/exceptions/NotFound.ts: -------------------------------------------------------------------------------- 1 | export class NotFound extends Error { 2 | constructor(message: string = 'Not found') { 3 | super(message); 4 | Object.setPrototypeOf(this, NotFound.prototype); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/core/sharedKernel/exceptions/Unauthorized.ts: -------------------------------------------------------------------------------- 1 | export class Unauthorized extends Error { 2 | constructor(message: string = 'Unauthorized') { 3 | super(message); 4 | Object.setPrototypeOf(this, Unauthorized.prototype); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/core/sharedKernel/interfaces/Converter.ts: -------------------------------------------------------------------------------- 1 | export interface Converter { 2 | from(from: F): T; 3 | to(to: T): F; 4 | } 5 | -------------------------------------------------------------------------------- /src/migrations/1566649710185-CreateUser.ts: -------------------------------------------------------------------------------- 1 | import {MigrationInterface, QueryRunner} from "typeorm"; 2 | 3 | export class CreateUser1566649710185 implements MigrationInterface { 4 | 5 | public async up(queryRunner: QueryRunner): Promise { 6 | const userTableQuery = ` 7 | CREATE TABLE IF NOT EXISTS users ( 8 | id SERIAL PRIMARY KEY, 9 | phone VARCHAR(20) NOT NULL, 10 | username VARCHAR(50) NOT NULL, 11 | notification_tokens VARCHAR(60)[] DEFAULT '{}' 12 | ); 13 | CREATE UNIQUE INDEX users_phonenumber_uindex 14 | ON "users" (phone); 15 | CREATE UNIQUE INDEX users_username_uindex 16 | ON "users" (username); 17 | `; 18 | 19 | await queryRunner.query(userTableQuery); 20 | await queryRunner.commitTransaction(); 21 | await queryRunner.startTransaction(); 22 | } 23 | 24 | public async down(queryRunner: QueryRunner): Promise { 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/migrations/1566649715982-CreateEvent.ts: -------------------------------------------------------------------------------- 1 | import {MigrationInterface, QueryRunner} from "typeorm"; 2 | 3 | export class CreateEvent1566649715982 implements MigrationInterface { 4 | 5 | public async up(queryRunner: QueryRunner): Promise { 6 | const typeEnumQuery = ` 7 | CREATE TYPE EVENT_TYPE AS ENUM ( 8 | 'WORK', 9 | 'PERSONAL' 10 | );`; 11 | 12 | const eventTableQuery = ` 13 | CREATE TABLE IF NOT EXISTS event ( 14 | id SERIAL PRIMARY KEY, 15 | title VARCHAR(255) NOT NULL, 16 | creator_id INT NOT NULL, 17 | event_type EVENT_TYPE, 18 | start_date TIMESTAMP, 19 | end_date TIMESTAMP, 20 | created_at TIMESTAMP DEFAULT now(), 21 | FOREIGN KEY (creator_id) REFERENCES users(id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE 22 | );`; 23 | 24 | await queryRunner.query(typeEnumQuery); 25 | await queryRunner.query(eventTableQuery); 26 | await queryRunner.commitTransaction(); 27 | await queryRunner.startTransaction(); 28 | } 29 | 30 | public async down(queryRunner: QueryRunner): Promise { 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/primaryAdapters/rest/RestContainerModule.ts: -------------------------------------------------------------------------------- 1 | import {ContainerModule, interfaces} from 'inversify'; 2 | import {UserController} from './user/UserController'; 3 | import {EventController} from './event/EventController'; 4 | import {HttpErrorHandler} from './common/HttpErrorHandlers'; 5 | 6 | export default new ContainerModule(( 7 | bind: interfaces.Bind, 8 | unbind: interfaces.Unbind, 9 | isBound: interfaces.IsBound, 10 | rebind: interfaces.Rebind 11 | ) => { 12 | bind(UserController).toSelf(); 13 | bind(EventController).toSelf(); 14 | bind(HttpErrorHandler).toSelf(); 15 | }); 16 | -------------------------------------------------------------------------------- /src/primaryAdapters/rest/common/HttpErrorHandlers.ts: -------------------------------------------------------------------------------- 1 | import {ApiError} from './data/ApiError'; 2 | import express = require('express'); 3 | import { Middleware, ExpressErrorMiddlewareInterface } from 'routing-controllers'; 4 | import { injectable } from 'inversify'; 5 | import { ValidationError } from 'class-validator'; 6 | import {NotFound} from '../../../core/sharedKernel/exceptions/NotFound'; 7 | import {Unauthorized} from '../../../core/sharedKernel/exceptions/Unauthorized'; 8 | import {BadRequest} from '../../../core/sharedKernel/exceptions/BadRequest'; 9 | import {Forbidden} from '../../../core/sharedKernel/exceptions/Forbidden'; 10 | 11 | interface HandlerResult { 12 | errors: ApiError[]; 13 | code: number; 14 | } 15 | 16 | interface ErrorHandler { 17 | handle(error: any, request: express.Request): HandlerResult; 18 | } 19 | 20 | class ValidationErrorHandler implements ErrorHandler { 21 | handle(error: any, request: express.Request): HandlerResult { 22 | console.error(error, 'Validation error'); 23 | return { 24 | errors: error 25 | .map(e => Object.values(e.constraints)) 26 | .reduce((acc, el) => acc.concat(el), []) 27 | .map(m => new ApiError(m)), 28 | code: 400 29 | }; 30 | } 31 | } 32 | 33 | class NotFoundErrorHandler implements ErrorHandler { 34 | handle(error: any, request: express.Request): HandlerResult { 35 | console.error(error, 'Not found'); 36 | return { 37 | errors: [new ApiError(error.message)], 38 | code: 404 39 | }; 40 | } 41 | } 42 | 43 | class BadRequestErrorHandler implements ErrorHandler { 44 | handle(error: any, request: express.Request): HandlerResult { 45 | console.error(error, 'Bad request'); 46 | return { 47 | errors: [new ApiError(error.message)], 48 | code: 400 49 | }; 50 | } 51 | } 52 | 53 | class UnauthorizedErrorHandler implements ErrorHandler { 54 | handle(error: any, request: express.Request): HandlerResult { 55 | console.error(error, 'Unauthorized'); 56 | return { 57 | errors: [new ApiError(error.message)], 58 | code: 401 59 | }; 60 | } 61 | } 62 | 63 | class ForbiddenErrorHandler implements ErrorHandler { 64 | handle(error: any, request: express.Request): HandlerResult { 65 | console.error(error, 'Forbidden'); 66 | return { 67 | errors: [new ApiError(error.message)], 68 | code: 403 69 | }; 70 | } 71 | } 72 | 73 | class UnhandledErrorHandler implements ErrorHandler { 74 | handle(error: any, request: express.Request) { 75 | console.error(error, 'Unhandled exception'); 76 | let message = error.message; 77 | return { 78 | errors: [new ApiError(message)], 79 | code: 500 80 | }; 81 | } 82 | } 83 | 84 | @injectable() 85 | @Middleware({type: 'after', priority: 1}) 86 | export class HttpErrorHandler implements ExpressErrorMiddlewareInterface { 87 | public error(error: any, request: express.Request, response: express.Response, next: express.NextFunction): void { 88 | const handler = this.getErrorHandler(error); 89 | let {code, errors} = handler.handle(error, request); 90 | response.status(code).json(errors); 91 | next(); 92 | } 93 | 94 | private getErrorHandler(error: any): ErrorHandler { 95 | if (Array.isArray(error) && error.every((element) => element instanceof ValidationError)) { 96 | return new ValidationErrorHandler(); 97 | } else if (error instanceof NotFound) { 98 | return new NotFoundErrorHandler(); 99 | } else if (error instanceof Unauthorized) { 100 | return new UnauthorizedErrorHandler(); 101 | } else if (error instanceof BadRequest) { 102 | return new BadRequestErrorHandler(); 103 | } else if (error instanceof Forbidden) { 104 | return new ForbiddenErrorHandler(); 105 | } else { 106 | return new UnhandledErrorHandler(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/primaryAdapters/rest/common/data/ApiError.ts: -------------------------------------------------------------------------------- 1 | export class ApiError { 2 | public message: string; 3 | 4 | constructor(message: string) { 5 | this.message = message; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/primaryAdapters/rest/event/EventController.ts: -------------------------------------------------------------------------------- 1 | import {inject, injectable} from 'inversify'; 2 | import {Body, CurrentUser, Get, JsonController, Param, Post} from 'routing-controllers'; 3 | import {EventCreationService} from '../../../core/component/event/application/services/EventCreationService'; 4 | import {EventQueryService} from '../../../core/component/event/application/services/EventQueryService'; 5 | import {EventResponse} from '../../../core/component/event/application/data/output/EventResponse'; 6 | import {EventCreationInput} from '../../../core/component/event/application/data/input/EventCreationInput'; 7 | import {validateOrReject} from 'class-validator'; 8 | 9 | @injectable() 10 | @JsonController('/event') 11 | export class EventController { 12 | private eventCreationService: EventCreationService; 13 | private eventQueryService: EventQueryService; 14 | 15 | constructor( 16 | @inject(EventCreationService) eventCreationService: EventCreationService, 17 | @inject(EventQueryService) eventQueryService: EventQueryService 18 | ) { 19 | this.eventCreationService = eventCreationService; 20 | this.eventQueryService = eventQueryService; 21 | } 22 | 23 | @Post() 24 | public async createEvent( 25 | @Body() input: EventCreationInput, 26 | @CurrentUser({required: true}) userId: number 27 | ): Promise { 28 | await validateOrReject(input); 29 | return await this.eventCreationService.createEvent(input, userId); 30 | } 31 | 32 | @Get('/user/my') 33 | public async getUsersEvents(@CurrentUser({required: true}) userId: number): Promise { 34 | return this.eventQueryService.getEventsCreatedByUser(userId); 35 | } 36 | 37 | @Get('/:id') 38 | public async getEventById( 39 | @Param('id') eventId: number, 40 | @CurrentUser({required: true}) userId: number 41 | ): Promise { 42 | return await this.eventQueryService.getEventById(eventId, userId); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/primaryAdapters/rest/user/UserController.ts: -------------------------------------------------------------------------------- 1 | import {Body, CurrentUser, JsonController, Post} from 'routing-controllers'; 2 | import {inject, injectable} from 'inversify'; 3 | import {UserRegistrationService} from '../../../core/component/user/application/services/UserRegistrationService'; 4 | import {UserRegistrationInput} from '../../../core/component/user/application/data/input/UserRegistrationInput'; 5 | import {validateOrReject} from 'class-validator'; 6 | import {UserResponse} from '../../../core/component/user/application/data/output/UserResponse'; 7 | import {UserNotificationTokenInput} from '../../../core/component/user/application/data/input/UserNotificationTokenInput'; 8 | import {UserNotificationService} from '../../../core/component/user/application/services/UserNotificationService'; 9 | 10 | @injectable() 11 | @JsonController('/user') 12 | export class UserController { 13 | private userRegistrationService: UserRegistrationService; 14 | private userNotificationService: UserNotificationService; 15 | 16 | constructor( 17 | @inject(UserRegistrationService) userRegistrationService: UserRegistrationService, 18 | @inject(UserNotificationService) userNotificationService: UserNotificationService 19 | ) { 20 | this.userRegistrationService = userRegistrationService; 21 | this.userNotificationService = userNotificationService; 22 | } 23 | 24 | @Post() 25 | public async register(@Body() input: UserRegistrationInput): Promise { 26 | await validateOrReject(input); 27 | return this.userRegistrationService.registerUser(input); 28 | } 29 | 30 | @Post('/notification_token') 31 | public async addNotificationToken( 32 | @Body() input: UserNotificationTokenInput, 33 | @CurrentUser({required: true}) userId: number 34 | ): Promise { 35 | await validateOrReject(input); 36 | return await this.userNotificationService.addNotificationToken(input.notificationToken, userId); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/secondaryAdapters/notification/FCMNotificationAdapter.ts: -------------------------------------------------------------------------------- 1 | import {NotificationService} from '../../core/port/notification/NotificationService'; 2 | import {NotificationMessage} from '../../core/port/notification/data/NotificationMessage'; 3 | import {injectable} from 'inversify'; 4 | 5 | @injectable() 6 | export class FCMNotificationAdapter implements NotificationService { 7 | public async sendNotifications(message: NotificationMessage, tokens: string[]): Promise { 8 | console.log(`Notification "${message.title}" sent to tokens ${tokens.join(', ')}!`); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/secondaryAdapters/notification/NotificationContainerModule.ts: -------------------------------------------------------------------------------- 1 | import {ContainerModule, interfaces} from 'inversify'; 2 | import {NotificationService, NotificationServiceType} from '../../core/port/notification/NotificationService'; 3 | import {FCMNotificationAdapter} from './FCMNotificationAdapter'; 4 | 5 | export default new ContainerModule(( 6 | bind: interfaces.Bind, 7 | unbind: interfaces.Unbind, 8 | isBound: interfaces.IsBound, 9 | rebind: interfaces.Rebind 10 | ) => { 11 | bind(NotificationServiceType).to(FCMNotificationAdapter); 12 | }); 13 | -------------------------------------------------------------------------------- /src/secondaryAdapters/postgres/Connection.ts: -------------------------------------------------------------------------------- 1 | import {Connection, createConnection, getConnectionOptions} from 'typeorm'; 2 | 3 | export async function createConnectionPool(): Promise { 4 | const connectionOptions = await getConnectionOptions(); 5 | Object.assign(connectionOptions, { 6 | synchronize: false, 7 | logging: true, 8 | entities: ['dist/src/secondaryAdapters/postgres/**/*Entity.js'], 9 | migrations: ['dist/src/migrations/*.js'], 10 | migrationsDir: 'dist/src/migrations', 11 | migrationsRun: true 12 | }); 13 | return await createConnection(connectionOptions); 14 | } 15 | -------------------------------------------------------------------------------- /src/secondaryAdapters/postgres/PostgresContainerModule.ts: -------------------------------------------------------------------------------- 1 | import {ContainerModule, interfaces} from 'inversify'; 2 | import {EventEntityConverter} from './event/converters/EventEntityConverter'; 3 | import {EventRepository, EventRepositoryType} from '../../core/component/event/port/EventRepository'; 4 | import {EventRepositoryAdapter} from './event/repository/EventRepositoryAdapter'; 5 | import {UserEntityConverter} from './user/converters/UserEntityConverter'; 6 | import {UserRepository, UserRepositoryType} from '../../core/component/user/port/UserRepository'; 7 | import {UserRepositoryAdapter} from './user/repository/UserRepositoryAdapter'; 8 | 9 | export default new ContainerModule(( 10 | bind: interfaces.Bind, 11 | unbind: interfaces.Unbind, 12 | isBound: interfaces.IsBound, 13 | rebind: interfaces.Rebind 14 | ) => { 15 | bind(EventEntityConverter).toSelf(); 16 | bind(EventRepositoryType).to(EventRepositoryAdapter); 17 | 18 | bind(UserEntityConverter).toSelf(); 19 | bind(UserRepositoryType).to(UserRepositoryAdapter); 20 | }); 21 | -------------------------------------------------------------------------------- /src/secondaryAdapters/postgres/common/BaseTypeORMRepository.ts: -------------------------------------------------------------------------------- 1 | import {EntityManager, getManager, Repository} from 'typeorm'; 2 | import {injectable, unmanaged} from 'inversify'; 3 | 4 | @injectable() 5 | export class BaseTypeORMRepository { 6 | protected entity: new() => T; 7 | 8 | constructor(@unmanaged() entity: new() => T) { 9 | this.entity = entity; 10 | } 11 | 12 | protected get entityManager(): EntityManager { 13 | return getManager(); 14 | } 15 | 16 | protected get repository(): Repository { 17 | return this.entityManager.getRepository(this.entity); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/secondaryAdapters/postgres/event/converters/EventEntityConverter.ts: -------------------------------------------------------------------------------- 1 | import {Converter} from '../../../../core/sharedKernel/interfaces/Converter'; 2 | import {Event} from '../../../../core/component/event/domain/data/Event'; 3 | import {EventEntity} from '../data/EventEntity'; 4 | import {injectable} from 'inversify'; 5 | 6 | @injectable() 7 | export class EventEntityConverter implements Converter { 8 | from(from: EventEntity): Event { 9 | return Event.fromObject({ 10 | ...from 11 | }); 12 | } 13 | 14 | to(to: Event): EventEntity { 15 | return EventEntity.fromObject({ 16 | ...to 17 | }); 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/secondaryAdapters/postgres/event/data/EventEntity.ts: -------------------------------------------------------------------------------- 1 | import {Column, CreateDateColumn, Entity, PrimaryColumn, PrimaryGeneratedColumn} from 'typeorm'; 2 | import {EventType} from '../../../../core/component/event/domain/data/EventType'; 3 | 4 | interface EventBuilderObj { 5 | id?: number | null | undefined, 6 | title: string, 7 | startDate: Date, 8 | endDate: Date, 9 | creatorId: number, 10 | eventType: EventType, 11 | createdAt?: Date | null | undefined 12 | } 13 | 14 | @Entity({name: 'event'}) 15 | export class EventEntity { 16 | @PrimaryColumn({name: 'id', generated: true}) 17 | id?: number; 18 | 19 | @Column({name: 'title'}) 20 | title: string; 21 | 22 | @Column({name: 'start_date'}) 23 | startDate: Date; 24 | 25 | @Column({name: 'end_date'}) 26 | endDate: Date; 27 | 28 | @Column({name: 'creator_id'}) 29 | creatorId: number; 30 | 31 | @Column({name: 'event_type', type: 'enum', enum: EventType}) 32 | eventType: EventType; 33 | 34 | @CreateDateColumn({name: 'created_at'}) 35 | createdAt: Date = new Date(); 36 | 37 | public static fromObject(builder: EventBuilderObj): EventEntity { 38 | const event = new EventEntity(); 39 | event.id = builder.id || undefined; 40 | event.title = builder.title; 41 | event.startDate = builder.startDate; 42 | event.endDate = builder.endDate; 43 | event.creatorId = builder.creatorId; 44 | event.eventType = builder.eventType; 45 | if(builder.createdAt) event.createdAt = builder.createdAt; 46 | return event; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/secondaryAdapters/postgres/event/repository/EventRepositoryAdapter.ts: -------------------------------------------------------------------------------- 1 | import {EventRepository} from '../../../../core/component/event/port/EventRepository'; 2 | import {Event} from '../../../../core/component/event/domain/data/Event' 3 | import {inject, injectable} from 'inversify'; 4 | import {BaseTypeORMRepository} from '../../common/BaseTypeORMRepository'; 5 | import {EventEntity} from '../data/EventEntity'; 6 | import {Converter} from '../../../../core/sharedKernel/interfaces/Converter'; 7 | import {EventEntityConverter} from '../converters/EventEntityConverter'; 8 | import {Between} from 'typeorm'; 9 | 10 | @injectable() 11 | export class EventRepositoryAdapter extends BaseTypeORMRepository implements EventRepository { 12 | private entityConverter: Converter; 13 | 14 | constructor( 15 | @inject(EventEntityConverter) entityConverter: Converter 16 | ) { 17 | super(EventEntity); 18 | this.entityConverter = entityConverter; 19 | } 20 | 21 | public async findByCreator(creatorId: number): Promise { 22 | const entities = await this.repository.find({where: {creatorId}}); 23 | return entities.map(entity => this.entityConverter.from(entity)); 24 | } 25 | 26 | public async findById(id: number): Promise { 27 | const maybeEntity = await this.repository.findOne({where: {id}}); 28 | return maybeEntity ? this.entityConverter.from(maybeEntity) : null; 29 | } 30 | 31 | public async findByStartDateRange(dateFrom: Date, dateTo: Date): Promise { 32 | const entities = await this.repository.find({where: {startDate: Between(dateFrom, dateTo)}}); 33 | return entities.map(entity => this.entityConverter.from(entity)); 34 | } 35 | 36 | public async save(event: Event): Promise { 37 | const entity = this.entityConverter.to(event); 38 | const savedEntity = await this.repository.save(entity); 39 | return this.entityConverter.from(savedEntity); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/secondaryAdapters/postgres/user/converters/UserEntityConverter.ts: -------------------------------------------------------------------------------- 1 | import {injectable} from 'inversify'; 2 | import {Converter} from '../../../../core/sharedKernel/interfaces/Converter'; 3 | import {UserEntity} from '../data/UserEntity'; 4 | import {User} from '../../../../core/component/user/domain/data/User'; 5 | 6 | @injectable() 7 | export class UserEntityConverter implements Converter { 8 | from(from: UserEntity): User { 9 | return User.fromObject({...from}); 10 | } 11 | 12 | to(to: User): UserEntity { 13 | return UserEntity.fromObject({...to}); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/secondaryAdapters/postgres/user/data/UserEntity.ts: -------------------------------------------------------------------------------- 1 | import {Column, Entity, PrimaryColumn} from 'typeorm'; 2 | 3 | interface UserBuilderObj { 4 | id?: number | undefined | null; 5 | 6 | phone: string; 7 | 8 | username: string; 9 | 10 | notificationTokens?: string[] | undefined | null; 11 | } 12 | 13 | @Entity({name: 'users'}) 14 | export class UserEntity { 15 | @PrimaryColumn({name: 'id', generated: true}) 16 | id?: number; 17 | 18 | @Column({name: 'phone'}) 19 | phone: string; 20 | 21 | @Column({name: 'username'}) 22 | username: string; 23 | 24 | @Column('text', {name: 'notification_tokens', array: true}) 25 | notificationTokens: string[] = []; 26 | 27 | public static fromObject(builder: UserBuilderObj): UserEntity { 28 | const user = new UserEntity(); 29 | user.id = builder.id || undefined; 30 | user.phone = builder.phone; 31 | user.username = builder.username; 32 | if (builder.notificationTokens != null) user.notificationTokens = builder.notificationTokens; 33 | return user; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/secondaryAdapters/postgres/user/repository/UserRepositoryAdapter.ts: -------------------------------------------------------------------------------- 1 | import {inject, injectable} from 'inversify'; 2 | import {BaseTypeORMRepository} from '../../common/BaseTypeORMRepository'; 3 | import {UserEntity} from '../data/UserEntity'; 4 | import {Converter} from '../../../../core/sharedKernel/interfaces/Converter'; 5 | import {UserRepository} from '../../../../core/component/user/port/UserRepository'; 6 | import {User} from '../../../../core/component/user/domain/data/User'; 7 | import {UserEntityConverter} from '../converters/UserEntityConverter'; 8 | 9 | @injectable() 10 | export class UserRepositoryAdapter extends BaseTypeORMRepository implements UserRepository { 11 | private entityConverter: Converter; 12 | 13 | constructor( 14 | @inject(UserEntityConverter) entityConverter: Converter 15 | ) { 16 | super(UserEntity); 17 | this.entityConverter = entityConverter; 18 | } 19 | 20 | public async save(event: User): Promise { 21 | const entity = this.entityConverter.to(event); 22 | const savedEntity = await this.repository.save(entity); 23 | return this.entityConverter.from(savedEntity); 24 | } 25 | 26 | public async findById(id: number): Promise { 27 | const maybeEntity = await this.repository.findOne({where: {id}}); 28 | return maybeEntity ? this.entityConverter.from(maybeEntity) : null; 29 | } 30 | 31 | public async findByUsername(username: string): Promise { 32 | const maybeEntity = await this.repository.findOne({where: {username}}); 33 | return maybeEntity ? this.entityConverter.from(maybeEntity) : null; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | import appInitializer from './AppInitilizer'; 2 | import logger from './configuration/Logger'; 3 | import config from './configuration/Config'; 4 | import * as events from 'events'; 5 | 6 | 7 | class Server { 8 | 9 | private server: any; 10 | private readonly port: number; 11 | 12 | public constructor() { 13 | this.port = config.port; 14 | } 15 | 16 | public async runServer(): Promise { 17 | try { 18 | const app = await appInitializer.initialize(); 19 | this.server = app.listen(this.port); 20 | this.server.on('listening', () => { 21 | let address = this.server.address(); 22 | logger.info(`Listening on port ${address.port}`); 23 | }); 24 | 25 | this.server.on('error', (error: Error) => { 26 | logger.error('Server start error: ', error); 27 | process.exit(1); 28 | }); 29 | } catch (error) { 30 | logger.error(error); 31 | // TODO: Graceful shutdown 32 | } 33 | } 34 | } 35 | 36 | export default new Server().runServer(); 37 | -------------------------------------------------------------------------------- /test/acceptance/base.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import * as chai from 'chai'; 3 | import * as sinon from 'sinon'; 4 | import {SinonStub} from 'sinon'; 5 | import AppInitializer from '../../src/AppInitilizer'; 6 | import {Application} from 'express'; 7 | import {Container} from 'inversify'; 8 | import * as PostgresConnection from '../../src/secondaryAdapters/postgres/Connection'; 9 | import mocks, {resetAllMocks} from '../common/mocks'; 10 | import {instance} from 'ts-mockito'; 11 | import chaiHttp = require('chai-http'); 12 | import {UserRepository, UserRepositoryType} from '../../src/core/component/user/port/UserRepository'; 13 | import {EventRepository, EventRepositoryType} from '../../src/core/component/event/port/EventRepository'; 14 | import {NotificationService, NotificationServiceType} from '../../src/core/port/notification/NotificationService'; 15 | import {SinonFakeTimers} from 'sinon'; // tslint:disable-line 16 | 17 | chai.use(chaiHttp); 18 | 19 | export {chai}; 20 | 21 | export class AcceptanceSetupManager { 22 | private stubs: SinonStub[]; 23 | public clock: SinonFakeTimers; 24 | 25 | public async beforeAcceptance(): Promise { 26 | this.stubs = [ 27 | sinon.stub(PostgresConnection, 'createConnectionPool'), 28 | ]; 29 | 30 | await AppInitializer.initialize(); 31 | this.mockPostgresAdapters(AppInitializer.diContainer); 32 | this.mockFCM(AppInitializer.diContainer); 33 | 34 | this.clock = sinon.useFakeTimers(); 35 | 36 | return AppInitializer.app; 37 | } 38 | 39 | public afterAcceptance() { 40 | this.stubs.forEach(stub => stub.restore()); 41 | } 42 | 43 | public afterEachAcceptance() { 44 | this.clock.restore(); 45 | resetAllMocks(); 46 | } 47 | 48 | private mockPostgresAdapters(container: Container): void { 49 | // User 50 | container.rebind(UserRepositoryType) 51 | .toConstantValue(instance(mocks.postgres.user.userRepositoryMock)); 52 | 53 | // Event 54 | container.rebind(EventRepositoryType) 55 | .toConstantValue(instance(mocks.postgres.event.eventRepositoryMock)); 56 | } 57 | 58 | private mockFCM(container: Container): void { 59 | container.rebind(NotificationServiceType) 60 | .toConstantValue(instance(mocks.fcm.fcmServiceAdapter)); 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /test/acceptance/event/eventTest.spec.ts: -------------------------------------------------------------------------------- 1 | import {Application} from 'express'; 2 | import {AcceptanceSetupManager, chai} from '../base'; 3 | import mocks from '../../common/mocks'; 4 | import {User} from '../../../src/core/component/user/domain/data/User'; 5 | import {DEFAULT_EVENT, DEFAULT_USER} from '../../common/constructionObjects'; 6 | import {Event} from '../../../src/core/component/event/domain/data/Event'; 7 | import { when, deepEqual } from 'ts-mockito'; 8 | 9 | describe('event acceptance tests', () => { 10 | let app: Application; 11 | let setupManager: AcceptanceSetupManager; 12 | const userRepoMock = mocks.postgres.user.userRepositoryMock; 13 | const eventRepoMock = mocks.postgres.event.eventRepositoryMock; 14 | 15 | before(async () => { 16 | setupManager = new AcceptanceSetupManager(); 17 | app = await setupManager.beforeAcceptance(); 18 | }); 19 | 20 | after(async () => { 21 | await setupManager.afterAcceptance(); 22 | }); 23 | 24 | afterEach(async () => { 25 | setupManager.afterEachAcceptance(); 26 | }); 27 | 28 | it('should create event successfully', () => { 29 | const testUserId = 1; 30 | const testEventId = 2; 31 | const testEvent = Event.fromObject({ 32 | ...DEFAULT_EVENT, 33 | id: testEventId 34 | }); 35 | const body = { 36 | title: DEFAULT_EVENT.title, 37 | startDate: DEFAULT_EVENT.startDate, 38 | endDate: DEFAULT_EVENT.endDate, 39 | eventType: DEFAULT_EVENT.eventType 40 | }; 41 | 42 | when(eventRepoMock.save( 43 | deepEqual(Event.fromObject({ 44 | ...body, creatorId: testUserId 45 | })))) 46 | .thenResolve(testEvent); 47 | when(userRepoMock.findById(testUserId)).thenResolve(User.fromObject({...DEFAULT_USER})); 48 | 49 | return chai.request(app) 50 | .post(`/api/event`) 51 | .set('user_id', `${testUserId}`) 52 | .send(body) 53 | .then(res => { 54 | chai.expect(res).to.have.status(200); 55 | chai.expect(res.body.id).to.equal(testEventId); 56 | }); 57 | }); 58 | 59 | it('should return 400 on event creation if validation fails', () => { 60 | const testUserId = 1; 61 | const body = { 62 | startDate: DEFAULT_EVENT.startDate, 63 | endDate: DEFAULT_EVENT.endDate, 64 | eventType: DEFAULT_EVENT.eventType 65 | }; 66 | 67 | return chai.request(app) 68 | .post(`/api/event`) 69 | .set('user_id', `${testUserId}`) 70 | .send(body) 71 | .then(res => { 72 | chai.expect(res).to.have.status(400); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/acceptance/user/userTest.spec.ts: -------------------------------------------------------------------------------- 1 | import {Application} from 'express'; 2 | import {AcceptanceSetupManager, chai} from '../base'; 3 | import mocks from '../../common/mocks'; 4 | import {deepEqual, when} from 'ts-mockito'; 5 | import {DEFAULT_USER} from '../../common/constructionObjects'; 6 | import {User} from '../../../src/core/component/user/domain/data/User'; 7 | 8 | describe('user acceptance tests', () => { 9 | let app: Application; 10 | let setupManager: AcceptanceSetupManager; 11 | const userRepoMock = mocks.postgres.user.userRepositoryMock; 12 | 13 | before(async () => { 14 | setupManager = new AcceptanceSetupManager(); 15 | app = await setupManager.beforeAcceptance(); 16 | }); 17 | 18 | 19 | after(async () => { 20 | await setupManager.afterAcceptance(); 21 | }); 22 | 23 | afterEach(async () => { 24 | setupManager.afterEachAcceptance(); 25 | }); 26 | 27 | it('should complete registration successfully', () => { 28 | const testUserId = 1; 29 | const testUser = User.fromObject({ 30 | ...DEFAULT_USER, 31 | id: testUserId 32 | }); 33 | const body = { 34 | phone: DEFAULT_USER.phone, 35 | username: DEFAULT_USER.username 36 | }; 37 | 38 | 39 | when(userRepoMock.findByUsername(body.username)).thenResolve(null); 40 | when(userRepoMock.save(deepEqual(User.fromObject({...body})))).thenResolve(testUser); 41 | 42 | return chai.request(app) 43 | .post(`/api/user`) 44 | .send(body) 45 | .then(res => { 46 | chai.expect(res).to.have.status(200); 47 | chai.expect(res.body.id).to.equal(testUserId); 48 | }); 49 | }); 50 | 51 | it('should return 400 if username already exists', () => { 52 | const testUserId = 1; 53 | const testUser = User.fromObject({ 54 | ...DEFAULT_USER, 55 | id: testUserId 56 | }); 57 | const body = { 58 | phone: DEFAULT_USER.phone, 59 | username: DEFAULT_USER.username 60 | }; 61 | 62 | when(userRepoMock.findByUsername(body.username)).thenResolve(testUser); 63 | 64 | return chai.request(app) 65 | .post(`/api/user`) 66 | .send(body) 67 | .then(res => { 68 | chai.expect(res).to.have.status(400); 69 | }); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /test/common/constructionObjects.ts: -------------------------------------------------------------------------------- 1 | import {EventType} from '../../src/core/component/event/domain/data/EventType'; 2 | 3 | const DEFAULT_USER = { 4 | id: 1, 5 | phone: '+380635555555', 6 | username: 'username', 7 | notificationTokens: [] 8 | }; 9 | 10 | const DEFAULT_EVENT = { 11 | id: 1, 12 | title: 'Test-egg', 13 | startDate: new Date(2019, 1, 1), 14 | endDate: new Date(2019, 1, 2), 15 | creatorId: 1, 16 | eventType: EventType.PERSONAL, 17 | createdAt: new Date(2018, 1, 1) 18 | }; 19 | 20 | export { 21 | DEFAULT_USER, 22 | DEFAULT_EVENT 23 | } 24 | -------------------------------------------------------------------------------- /test/common/mocks.ts: -------------------------------------------------------------------------------- 1 | import {mock, reset} from 'ts-mockito'; 2 | import {EventRepositoryAdapter} from '../../src/secondaryAdapters/postgres/event/repository/EventRepositoryAdapter'; 3 | import {UserRepositoryAdapter} from '../../src/secondaryAdapters/postgres/user/repository/UserRepositoryAdapter'; 4 | import {FCMNotificationAdapter} from '../../src/secondaryAdapters/notification/FCMNotificationAdapter'; 5 | import * as R from 'ramda'; 6 | 7 | const mocks = { 8 | postgres: { 9 | user: { 10 | userRepositoryMock: mock(UserRepositoryAdapter) 11 | }, 12 | event: { 13 | eventRepositoryMock: mock(EventRepositoryAdapter) 14 | }, 15 | }, 16 | fcm: { 17 | fcmServiceAdapter: mock(FCMNotificationAdapter) 18 | } 19 | }; 20 | 21 | export default mocks; 22 | 23 | export function resetAllMocks() { 24 | const mockPaths = [ 25 | ['postgres', 'user', 'userRepositoryMock'], 26 | ['postgres', 'event', 'eventRepositoryMock'], 27 | ['fcm', 'fcmServiceAdapter'], 28 | ]; 29 | // @ts-ignore 30 | const valuesFromPath = R.curry((paths, obj) => R.ap([R.path(R.__, obj)], paths)); 31 | valuesFromPath(mockPaths, mocks).forEach(reset); 32 | } 33 | -------------------------------------------------------------------------------- /test/unit/base.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import * as chai from 'chai'; 3 | 4 | chai.should(); 5 | 6 | export {chai}; 7 | -------------------------------------------------------------------------------- /test/unit/event/eventCreationServiceTest.spec.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../../../src/core/component/event/domain/data/Event'; 2 | import {DEFAULT_EVENT} from '../../common/constructionObjects'; 3 | import mocks, {resetAllMocks} from '../../common/mocks'; 4 | import {chai} from '../base'; 5 | import {EventCreationService} from '../../../src/core/component/event/application/services/EventCreationService'; 6 | import {deepEqual, instance, mock, reset, verify, when} from 'ts-mockito'; 7 | import {EventReminderService} from '../../../src/core/component/event/application/services/EventReminderService'; 8 | import {EventConverter} from '../../../src/core/component/event/application/services/converters/EventConverter'; 9 | 10 | describe('event creation service tests', () => { 11 | let eventService: EventCreationService; 12 | const eventRepoMock = mocks.postgres.event.eventRepositoryMock; 13 | const reminderServiceMock = mock(EventReminderService); 14 | 15 | before(async () => { 16 | eventService = new EventCreationService( 17 | instance(eventRepoMock), 18 | instance(reminderServiceMock), 19 | new EventConverter(), 20 | ); 21 | }); 22 | 23 | afterEach(() => { 24 | resetAllMocks(); 25 | reset(reminderServiceMock); 26 | }); 27 | 28 | it('should create event', async () => { 29 | const testUserId = 1; 30 | const testEventId = 2; 31 | const testEvent = Event.fromObject({ 32 | ...DEFAULT_EVENT, 33 | id: testEventId 34 | }); 35 | const input = { 36 | title: DEFAULT_EVENT.title, 37 | startDate: DEFAULT_EVENT.startDate, 38 | endDate: DEFAULT_EVENT.endDate, 39 | eventType: DEFAULT_EVENT.eventType 40 | }; 41 | 42 | when(eventRepoMock.save( 43 | deepEqual(Event.fromObject({ 44 | ...input, creatorId: testUserId 45 | })))) 46 | .thenResolve(testEvent); 47 | 48 | const response = await eventService.createEvent(input, testUserId); 49 | 50 | chai.expect(response.id).to.eql(testEvent.id); 51 | verify(reminderServiceMock.setupReminder(deepEqual(testEvent))).once(); 52 | }); 53 | 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /test/unit/event/eventReminderServiceTest.spec.ts: -------------------------------------------------------------------------------- 1 | import {Event} from '../../../src/core/component/event/domain/data/Event'; 2 | import {DEFAULT_EVENT, DEFAULT_USER} from '../../common/constructionObjects'; 3 | import mocks, {resetAllMocks} from '../../common/mocks'; 4 | import {anything, deepEqual, instance, verify, when} from 'ts-mockito'; 5 | import {EventReminderService} from '../../../src/core/component/event/application/services/EventReminderService'; 6 | import {User} from '../../../src/core/component/user/domain/data/User'; 7 | import sinon = require('sinon'); 8 | 9 | describe('event reminder service tests', () => { 10 | let eventService: EventReminderService; 11 | const userRepoMock = mocks.postgres.user.userRepositoryMock; 12 | const notificationServiceMock = mocks.fcm.fcmServiceAdapter; 13 | let clock; 14 | 15 | before(async () => { 16 | eventService = new EventReminderService( 17 | instance(notificationServiceMock), 18 | instance(userRepoMock) 19 | ); 20 | clock = sinon.useFakeTimers(); 21 | }); 22 | 23 | afterEach(() => { 24 | resetAllMocks(); 25 | clock.reset(); 26 | }); 27 | 28 | it('should setup reminder', async () => { 29 | const testUserId = 1; 30 | const testEventId = 2; 31 | const testEvent = Event.fromObject({ 32 | ...DEFAULT_EVENT, 33 | id: testEventId 34 | }); 35 | const testUser = User.fromObject({ 36 | ...DEFAULT_USER, 37 | notificationTokens: ['token123'] 38 | }); 39 | 40 | when(userRepoMock.findById(testUserId)).thenResolve(testUser); 41 | 42 | await eventService.setupReminder(testEvent); 43 | clock.tick(1); 44 | 45 | const expectedMessage = { 46 | title: `It is time to ${testEvent.title}`, 47 | body: 'Hello from JSFest!', 48 | data: {eventId: testEvent.id} 49 | }; 50 | verify(notificationServiceMock.sendNotifications(deepEqual(expectedMessage), anything())).once(); 51 | }); 52 | 53 | 54 | }); 55 | -------------------------------------------------------------------------------- /test/unit/user/userRegistrationServiceTest.spec.ts: -------------------------------------------------------------------------------- 1 | import {chai} from '../base'; 2 | import mocks, {resetAllMocks} from '../../common/mocks'; 3 | import {deepEqual, instance, when} from 'ts-mockito'; 4 | import {UserRegistrationService} from '../../../src/core/component/user/application/services/UserRegistrationService'; 5 | import {UserConverter} from '../../../src/core/component/user/application/services/converters/UserConverter'; 6 | import {User} from '../../../src/core/component/user/domain/data/User'; 7 | import {DEFAULT_USER} from '../../common/constructionObjects'; 8 | 9 | describe('user registration service tests', () => { 10 | let userService: UserRegistrationService; 11 | const userRepoMock = mocks.postgres.user.userRepositoryMock; 12 | 13 | before(async () => { 14 | userService = new UserRegistrationService( 15 | instance(userRepoMock), 16 | new UserConverter(), 17 | ); 18 | }); 19 | 20 | afterEach(() => { 21 | resetAllMocks(); 22 | }); 23 | 24 | it('should register user', async () => { 25 | const testUserId = 1; 26 | const testUser = User.fromObject({ 27 | ...DEFAULT_USER, 28 | id: testUserId 29 | }); 30 | const input = { 31 | phone: DEFAULT_USER.phone, 32 | username: DEFAULT_USER.username 33 | }; 34 | 35 | when(userRepoMock.findByUsername(input.username)).thenResolve(null); 36 | when(userRepoMock.save(deepEqual(User.fromObject({...input})))).thenResolve(testUser); 37 | 38 | const response = await userService.registerUser(input); 39 | 40 | chai.expect(response.id).to.eql(testUserId); 41 | }); 42 | 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "pretty": true, 6 | "sourceMap": true, 7 | "target": "es6", 8 | "removeComments": true, 9 | "lib": [ 10 | "es2017", 11 | "es7", 12 | "es6", 13 | "dom" 14 | ], 15 | "outDir": "./dist", 16 | "baseUrl": "./src", 17 | "strictNullChecks": true, 18 | "noImplicitAny": false, 19 | "noFallthroughCasesInSwitch": true, 20 | "noImplicitReturns": true, 21 | "paths": { 22 | "*": [ 23 | "node_modules/*" 24 | ] 25 | }, 26 | "emitDecoratorMetadata": true, 27 | "experimentalDecorators": true 28 | }, 29 | "include": [ 30 | "src/**/*.ts", 31 | "test/**/**.ts" 32 | ], 33 | "exclude": [ 34 | "node_modules" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "align": [ 4 | true, 5 | "parameters", 6 | "arguments", 7 | "statements" 8 | ], 9 | "ban": false, 10 | "class-name": true, 11 | "comment-format": [ 12 | true, 13 | "check-space" 14 | ], 15 | "curly": false, 16 | "eofline": true, 17 | "forin": true, 18 | "indent": [ 19 | true, 20 | "spaces" 21 | ], 22 | "linebreak-style": [true, "LF"], 23 | "interface-name": false, 24 | "jsdoc-format": true, 25 | "label-position": true, 26 | "max-line-length": [ 27 | true, 28 | 140 29 | ], 30 | "member-ordering": [ 31 | true, 32 | "public-before-private", 33 | "variables-before-functions" 34 | ], 35 | "no-any": false, 36 | "no-arg": true, 37 | "no-bitwise": true, 38 | "no-console": [ 39 | true, 40 | "debug", 41 | "info", 42 | "time", 43 | "timeEnd", 44 | "trace" 45 | ], 46 | "no-floating-promises": true, 47 | "no-construct": true, 48 | "no-constructor-vars": false, 49 | "no-debugger": true, 50 | "no-shadowed-variable": true, 51 | "no-duplicate-variable": true, 52 | "no-eval": true, 53 | "no-internal-module": true, 54 | "no-require-imports": true, 55 | "no-string-literal": true, 56 | "no-switch-case-fall-through": true, 57 | "no-trailing-whitespace": true, 58 | "no-unused-expression": true, 59 | "no-use-before-declare": true, 60 | "no-var-keyword": true, 61 | "no-var-requires": false, 62 | "one-line": [ 63 | true, 64 | "check-open-brace", 65 | "check-catch", 66 | "check-else", 67 | "check-whitespace" 68 | ], 69 | "quotemark": [ 70 | true, 71 | "single" 72 | ], 73 | "radix": true, 74 | "semicolon": [ 75 | true, 76 | "always" 77 | ], 78 | "switch-default": true, 79 | "triple-equals": [ 80 | true, 81 | "allow-null-check" 82 | ], 83 | "typedef": [ 84 | false, 85 | "call-signature", 86 | "parameter", 87 | "property-declaration", 88 | "member-variable-declaration" 89 | ], 90 | "typedef-whitespace": [ 91 | true, { 92 | "call-signature": "nospace", 93 | "index-signature": "nospace", 94 | "parameter": "nospace", 95 | "property-declaration": "nospace", 96 | "variable-declaration": "nospace" 97 | } 98 | ], 99 | "use-strict": [ 100 | false, 101 | "check-module", 102 | "check-function" 103 | ], 104 | "variable-name": [ 105 | true, 106 | "ban-keywords" 107 | ], 108 | "whitespace": [ 109 | true, 110 | "check-branch", 111 | "check-decl", 112 | "check-operator", 113 | "check-separator", 114 | "check-type" 115 | ] 116 | } 117 | } 118 | --------------------------------------------------------------------------------