├── .editorconfig ├── .github └── pull_request_template.md ├── .gitignore ├── .npmignore ├── .prettierrc ├── .vscode └── settings.json ├── README.md ├── package-lock.json ├── package.json ├── src ├── index.ts └── modules │ ├── application-tokens.const.ts │ ├── classes │ ├── axios-exception-error.ts │ ├── constructed-object.ts │ ├── document-service-error.ts │ ├── index.ts │ └── sql-exception-error.ts │ ├── constants │ ├── do-not-validate.const.ts │ └── index.ts │ ├── decorators │ ├── api-docs │ │ ├── fetch-all-response.factory.ts │ │ ├── hive-api-docs.decorator.ts │ │ ├── hive-api-property.decorator.ts │ │ └── index.ts │ ├── critical.decorator.ts │ ├── index.ts │ ├── inject-metadata │ │ ├── index.ts │ │ ├── inject-metadata.decorator.ts │ │ ├── injected-dto.type.ts │ │ └── injected-metadata.interface.ts │ ├── permissions.decorator.ts │ ├── query-user.decorator.ts │ ├── rpc │ │ ├── get-message-options.ts │ │ ├── get-message-pattern.ts │ │ ├── hive-message-handler.decorator.ts │ │ ├── hive-message.decorator.ts │ │ └── index.ts │ └── user.decorator.ts │ ├── dtos │ ├── index.ts │ ├── pagination-search.dto.ts │ └── pagination.dto.ts │ ├── enums │ ├── index.ts │ └── passport-whitelisted-errors.enum.ts │ ├── exceptions │ ├── axios.exception.ts │ ├── bad-request.exception.ts │ ├── base.exception.ts │ ├── conflict.exception.ts │ ├── document-service.exception.ts │ ├── forbidden.exception.ts │ ├── index.ts │ ├── logged.exception.ts │ ├── not-found.exception.ts │ ├── passive.exception.ts │ ├── processing.exception.ts │ ├── redirect.exception.ts │ ├── redis.exception.ts │ ├── sql.exception.ts │ ├── sso.exception.ts │ ├── unauthorized.exception.ts │ └── validation.exception.ts │ ├── filters │ ├── base-http-exception.filter.ts │ ├── index.ts │ ├── logged-http-exception.filter.ts │ ├── passive-http-exception.filter.ts │ ├── redirect-http-exception.filter.ts │ └── uncaught-exception.filter.ts │ ├── functions │ ├── cookie-to-bearer.function.ts │ ├── get-validation.function.ts │ ├── index.ts │ ├── setup-swagger-docs.function.ts │ └── write-json.function.ts │ ├── guards │ ├── index.ts │ ├── passport-auth.guard.ts │ └── permissions.guard.ts │ ├── index.ts │ ├── interfaces │ ├── activemq-microservice-context.interface.ts │ ├── breadcrum.interface.ts │ ├── hive-api-docs-configuration.interface.ts │ ├── index.ts │ ├── pagination-options.interface.ts │ └── sequelize-error.interface.ts │ ├── modules │ ├── error-handler.module.ts │ ├── filter.module.ts │ ├── index.ts │ └── redis.module.ts │ ├── pipes │ ├── identity-array-validation.pipe.ts │ ├── identity-validation.pipe.ts │ ├── index.ts │ └── validation.pipe.ts │ ├── providers │ ├── error-handler-configuration │ │ ├── error-handler-configuration.provider.ts │ │ └── index.ts │ ├── filter │ │ ├── filter.provider.ts │ │ └── index.ts │ ├── index.ts │ ├── logger │ │ ├── index.ts │ │ └── logger.provider.ts │ ├── redis-configuration │ │ ├── index.ts │ │ └── redis-configuration.provider.ts │ └── redis │ │ ├── index.ts │ │ └── redis.provider.ts │ ├── services │ ├── error-handler │ │ ├── error-handler.service.ts │ │ └── index.ts │ ├── index.ts │ └── redis │ │ ├── index.ts │ │ └── redis.service.ts │ ├── types │ ├── index.ts │ └── sort-direction.type.ts │ ├── utility │ ├── index.ts │ └── throw-validation-errors.ts │ └── validators │ ├── index.ts │ └── is-number.validator.ts ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | #### Short description of what this resolves: 2 | 3 | 4 | ### PR Checklist 5 | - [ ] All `TODO`'s are done and removed 6 | - [ ] `package.json` has correct information 7 | - [ ] All dependencies are of correct type (regular vs peer vs dev) 8 | - [ ] Documented any complicated or confusing code 9 | - [ ] No linter errors 10 | - [ ] Project packages successfully (`npm pack`) 11 | - [ ] Installed local packed version in another project and tested 12 | - [ ] Updated README 13 | 14 | 15 | #### Changes proposed in this pull request: 16 | 17 | 18 | **JIRA Task Link:** -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.js 3 | *.d.ts 4 | *.tgz 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Misc 3 | ################# 4 | **/.DS_Store 5 | nbproject 6 | manifest.mf 7 | build.xml 8 | node_modules/* 9 | npm-debug.log 10 | /*.ts 11 | !*.d.ts 12 | tests 13 | examples 14 | .github 15 | coverage 16 | !*.metadata.json 17 | !bundles/*.js 18 | .vscode 19 | .editorconfig 20 | 21 | 22 | ################# 23 | ## JetBrains 24 | ################# 25 | .idea 26 | .project 27 | .settings 28 | 29 | ############ 30 | ## Windows 31 | ############ 32 | 33 | # Windows image file caches 34 | Thumbs.db 35 | 36 | # Folder config file 37 | Desktop.ini 38 | 39 | ############ 40 | ## Mac 41 | ############ 42 | 43 | # Mac crap 44 | .DS_Store 45 | 46 | 47 | /src 48 | *.tgz 49 | tsconfig.json 50 | tslint.json 51 | .git 52 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "tabWidth": 4, 4 | "jsxSingleQuote": true, 5 | "jsxBracketSameLine": true, 6 | "printWidth": 120, 7 | "trailingComma": "none" 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "dist/**": true 4 | }, 5 | "typescript.extension.sortImports.sortOnSave": true, 6 | "typescript.extension.sortImports.sortMethod": "path", 7 | "typescript.extension.sortImports.pathSortOrder": ["package", "relativeDownLevel", "relativeUpLevel"], 8 | "typescript.extension.sortImports.maxNamedImportsInSingleLine": 6, 9 | "cSpell.words": [ 10 | "Activemq", 11 | "breadcrum", 12 | "metatype", 13 | "microservices", 14 | "nestjs", 15 | "postpack", 16 | "postpublish", 17 | "teammaestro" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nestjs-common 2 | 3 | In this repo you will find a lot of the base shared code that we will user throughout all of our NestJS projects. Some of these common modules that we have bundled are: 4 | 5 | - TryCatch Decorators 6 | - HTTP Filters 7 | - Authentication Guards 8 | - Common Exceptions 9 | - Error Handling Service 10 | - Pagination Classes 11 | - Validation Pipes 12 | - Redis Service 13 | 14 | ## Breaking change for v20.0.0 15 | 16 | We are flipping the `Pagination` constructor so that you can utilize this class directly with NestJs `@Query()` instead of requiring you create a class every time. 17 | 18 | ## Installation 19 | 20 | ```sh 21 | npm i @teammaestro/nestjs-common 22 | ``` 23 | 24 | From there just add whatever you want to import into your Core/Common Modules 25 | 26 | ## Peer Dependencies 27 | 28 | There are several peer dependencies of this project. Once you install this package you will need to follow up and ensure the follow dependencies are installed: 29 | 30 | ```sh 31 | npm i @nestjs/common@^7.0 @nestjs/core@^7.0 @nestjs/passport@^7.0 @nestjs/testing@^7.0 @nestjs/microservices@^7.0 @nestjs/swagger class-validator@^0.12.2 config@^3.2 js-yaml@^3.0 log4js@^6.2 passport@^0.4 reflect-metadata@^0.1 rxjs@^6.5 32 | ``` 33 | 34 | ## Dev Dependencies 35 | 36 | There are also a few dev dependencies that you may want to add in order for typescript to compile correctly: 37 | 38 | ```sh 39 | npm i --save-dev @types/config @types/raven 40 | ``` 41 | 42 | ## Configurations 43 | 44 | There are several configurations that we use throughout our projects. Some of them are required by this package. Here is what you should add into the default config file (https://www.npmjs.com/package/config) 45 | 46 | ```yml 47 | application: 48 | name: AppName 49 | port: 8080 50 | apiPrefix: '/api' 51 | 52 | raven: 53 | dsn: 'https://logger.sentry.io/31' 54 | 55 | authentication: 56 | jwt: 57 | accessExpiration: 28800 # 8 hours 58 | refreshExpiration: 2592000 # 1 month 59 | session: 60 | accessCookie: 61 | name: 'access_token' 62 | options: 63 | httpOnly: true 64 | expires: false 65 | secure: true 66 | maxAge: 28800000 # 8 hours 67 | refreshCookie: 68 | name: 'refresh_token' 69 | options: 70 | httpOnly: true 71 | expires: false 72 | secure: true 73 | logger: 74 | appenders: 75 | out: 76 | type: stdout 77 | categories: 78 | default: 79 | appenders: 80 | - out 81 | level: error 82 | sql: 83 | appenders: 84 | - out 85 | level: error 86 | 87 | redis: 88 | host: 'localhost' 89 | keyPrefix: 'app_name_' 90 | expiration: 86400000 # ms - 24 hours 91 | ``` 92 | 93 | ## Available Modules 94 | 95 | ### Decorators 96 | 97 | There are a few different decorators that we have made available: 98 | 99 | #### @TryCatch(error: Error, options?: { customResponseMessage: string } ) 100 | 101 | This decorator will wrap your whole function into a try/catch and you can pass an optional custom error class for it to throw! Errors that are thrown 102 | that extend this package's BaseException are not re-wrapped. 103 | 104 | ```ts 105 | @TryCatch(SqlException) 106 | async fetchAll() { 107 | return await this.usersRepository.fetchAll() 108 | } 109 | ``` 110 | 111 | #### @QueryUser() 112 | 113 | This decorator will pull out the query parameters and the req.user object and inject them into the DTO 114 | 115 | ```ts 116 | async fetchAll(@QueryUser() query: FetchAllPgDto) { 117 | return await this.usersRepository.fetchAll(query) 118 | } 119 | ``` 120 | 121 | #### @User() 122 | 123 | This decorator will pull out the req.user object and make them available in the method 124 | 125 | ```ts 126 | async fetchAll(@User() user: AuthorizedUser) { 127 | return await this.usersRepository.fetchAll(user) 128 | } 129 | ``` 130 | 131 | #### @Permissions() 132 | 133 | This decorator will typically be used in tandem with the PermissionsGuard so that you can ensure the route is protected based on certain permissions 134 | 135 | ```ts 136 | @Permissions('CONTENT_VIEW') 137 | @UseGuards(IsLoggedInGuard, PermissionsGuard) 138 | @Get() 139 | async fetchAll(@Query() query: ContentFetchAllPgDto) { 140 | const content = await this.contentService.fetchAll(query); 141 | 142 | return new ContentFetchAllAndCount(content); 143 | } 144 | ``` 145 | 146 | #### @InjectMetadata() 147 | 148 | This decorator will inject metadata into an object on the the request. 149 | 150 | ```ts 151 | @Get() 152 | async fetchAll( 153 | @InjectMetadata('query', injectUser) assignmentPgDto: AdminDto 154 | ) { 155 | const assignments = await this.adminAssignmentService.fetchAll(assignmentPgDto); 156 | return findAndCountAllResponse(assignments, AdminAssignmentFetchAllResponse); 157 | } 158 | ``` 159 | 160 | In the example above, the user can be injected into the request's query so the Dto has access to all of that data while being built automatically by Nest. 161 | 162 | The decorator accepts the following arguments: 163 | 164 | ```ts 165 | (reqProperty?: string, ...injectFunctions: (req, paramTarget, paramProperty, paramIndex) => object)[] 166 | ``` 167 | 168 | where `reqProperty` is the name of the property on the request to inject data into and the `injectFunctions` are functions that given the request and the decorator data for the decorated method return an object to be merged into the metadata for the request property's value. 169 | 170 | ##### Recommend Usage 171 | 172 | In your project, it is recommend that you create an `InjectableMetadata` interface that defines all of the possible metadata that could be injected into request property. 173 | These should match up with the keys that the inject functions return their data on. Below is an example of what that might look like. 174 | 175 | ```ts 176 | export const injectUser = (req) => ({ user: req.user }); 177 | 178 | export const injectIsAdmin = (_req, target) => ({ isAdmin: Reflect.getMetadata('isAdmin', target.constructor) }); 179 | 180 | export interface InjectableMetadata { 181 | user: AuthenticatedUser; 182 | isAdmin: boolean; 183 | } 184 | ``` 185 | 186 | To access this data in your DTO, you should redefine the InjectedDto type provided by this library and use your InjectableMetadata interface. 187 | 188 | ```ts 189 | import { InjectedDto as CommonInjectedDto } from '@teammaestro/nestjs-common'; 190 | import { InjectableMetadata } from './injectable-metadata'; 191 | 192 | export type InjectedDto = CommonInjectedDto< 193 | DtoType, 194 | InjectableMetadata, 195 | PickedFields 196 | >; 197 | ``` 198 | 199 | Then in the DTO, use this as the type of the argument that is passed into the constructor. All of the injected metadata can then be found and 200 | appropriately typed on the `INJECTED_METADATA_KEY`. 201 | 202 | ```ts 203 | import { INJECTED_METADATA_KEY } from '@teammaestro/nestjs-common'; 204 | import { InjectedDto } from '../../common'; 205 | 206 | export class AdminDto { 207 | constructor(options: InjectedDto) { 208 | const { user, isAdmin: adminMode } = options[INJECTED_METADATA_KEY]; 209 | } 210 | } 211 | ``` 212 | 213 | ### Data Transfer Objects (Dtos) 214 | 215 | #### Pagination 216 | 217 | This class is our standard that we use for pagination requests. You will want to extend this class for your custom pagination classes 218 | 219 | _Note: If your application tends to sort by 'ASC', in your main.ts set the static property `defaultSortDir = 'ASC'`_ 220 | 221 | ```ts 222 | export class UserFetchAllPgDto extends Pagination { 223 | @IsOptional() 224 | @IsString() 225 | @IsIn(['firstName', 'lastName']) 226 | sortBy: string; 227 | 228 | @ValidateNested() 229 | filter: UserFetchAllFilter; 230 | 231 | constructor(pagination: PaginationOptions = {}) { 232 | super(pagination, 'firstName'); 233 | 234 | try { 235 | this.filter = new UserFetchAllFilter(JSON.parse(decodeURI(pagination.filter))); 236 | } catch (e) { 237 | this.filter = new UserFetchAllFilter(); 238 | } 239 | 240 | this.search = new Search(UserSearch, pagination.search).include || null; 241 | } 242 | } 243 | ``` 244 | 245 | ### Guards 246 | 247 | #### PassportAuthGuard 248 | 249 | This guard extends the @nestjs/passport AuthGuard and provides a better error handling pattern. You will want to extend this class for any custom guards in your application and override the handleErrors method to report any non-whitelisted errors. You can also customize the whitelisted errors with the addToWhitelist and removeFromWhitelist methods. By default, this will throw an UnauthorizedException, but you can change that functionality by overriding the throwException method. 250 | 251 | ```ts 252 | import { AuthGuard } from '@nestjs/passport'; 253 | import { UnauthorizedException } from '../exceptions'; 254 | import { PassportWhitelistedErrors } from '../enums'; 255 | 256 | export function PassportAuthGuard(strategyToken: string) { 257 | return class Guard extends AuthGuard(strategyToken) { 258 | whitelistedErrors: Set; 259 | 260 | constructor() { 261 | super(); 262 | 263 | this.whitelistedErrors = new Set(Object.values(PassportWhitelistedErrors)); 264 | } 265 | 266 | handleRequest(error, user, passportError) { 267 | if (error || !user) { 268 | // if the error is not an instance of our UnauthorizedException, then capture 269 | if (!(error instanceof UnauthorizedException)) { 270 | const actualError = 271 | error || passportError instanceof Error ? passportError : false || new Error('Passport Error'); 272 | 273 | // only handle (report to sentry) if not a whitelisted error 274 | if (!this.isWhitelisted(actualError)) { 275 | this.handleErrors(actualError); 276 | } 277 | } 278 | 279 | this.throwException(); 280 | } 281 | 282 | return user; 283 | } 284 | 285 | isWhitelisted(error = {} as Error) { 286 | return Array.from(this.whitelistedErrors).some((whitelistedError) => { 287 | if (typeof error.message === 'string') { 288 | return error.message.includes(whitelistedError); 289 | } 290 | return false; 291 | }); 292 | } 293 | 294 | handleErrors(error: Error) { 295 | console.warn('Override the handleErrors method in the child class of PassportAuthGuard to report errors!'); 296 | console.error(error); 297 | } 298 | 299 | throwException() { 300 | throw new UnauthorizedException(); 301 | } 302 | 303 | addToWhitelist(messages: string[]) { 304 | messages.forEach((message) => this.whitelistedErrors.add(message)); 305 | } 306 | 307 | removeFromWhitelist(messages: PassportWhitelistedErrors[]) { 308 | messages.forEach((message) => this.whitelistedErrors.delete(String(message))); 309 | } 310 | }; 311 | } 312 | ``` 313 | 314 | ## Distribution 315 | 316 | ```sh 317 | npm pack 318 | npm version (major|minor|patch) 319 | npm publish 320 | ``` 321 | 322 | _Note: This will also automatically push changes and tags post-publish_ 323 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@teammaestro/nestjs-common", 3 | "version": "20.1.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.8.3", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", 10 | "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.8.3" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.9.5", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", 19 | "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.9.0", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", 25 | "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.9.0", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | } 32 | }, 33 | "@nestjs/common": { 34 | "version": "8.2.0", 35 | "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-8.2.0.tgz", 36 | "integrity": "sha512-Vska6eEQtnwM9CBnL7QINQUE8Xq+d4s/CXkpxrUab1HgTA9m5LjmkCPdLw7tBFxMQ6Px+kmcXkQI3rKMfq9Wzw==", 37 | "dev": true, 38 | "requires": { 39 | "axios": "0.24.0", 40 | "iterare": "1.2.1", 41 | "tslib": "2.3.1", 42 | "uuid": "8.3.2" 43 | }, 44 | "dependencies": { 45 | "tslib": { 46 | "version": "2.3.1", 47 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", 48 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", 49 | "dev": true 50 | }, 51 | "uuid": { 52 | "version": "8.3.2", 53 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 54 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 55 | "dev": true 56 | } 57 | } 58 | }, 59 | "@nestjs/core": { 60 | "version": "8.2.0", 61 | "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-8.2.0.tgz", 62 | "integrity": "sha512-TjN705n0DQrcL8hN1021dITol2BgzIT+FFv3RPKJWMwck8y0ebWK0PTI4vXeLial75fPd+4K4LhL6nAO4WyjFw==", 63 | "dev": true, 64 | "requires": { 65 | "@nuxtjs/opencollective": "0.3.2", 66 | "fast-safe-stringify": "2.1.1", 67 | "iterare": "1.2.1", 68 | "object-hash": "2.2.0", 69 | "path-to-regexp": "3.2.0", 70 | "tslib": "2.3.1", 71 | "uuid": "8.3.2" 72 | }, 73 | "dependencies": { 74 | "tslib": { 75 | "version": "2.3.1", 76 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", 77 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", 78 | "dev": true 79 | }, 80 | "uuid": { 81 | "version": "8.3.2", 82 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 83 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 84 | "dev": true 85 | } 86 | } 87 | }, 88 | "@nestjs/microservices": { 89 | "version": "8.2.0", 90 | "resolved": "https://registry.npmjs.org/@nestjs/microservices/-/microservices-8.2.0.tgz", 91 | "integrity": "sha512-wqrqp//eN4YcfYcyIfv6wtPWWsP/aTxLI4VHYN7n26LSeI1KKiEmZnEdBNl1fW/pnl/HIYnjlUu6hmth338Lrw==", 92 | "dev": true, 93 | "requires": { 94 | "iterare": "1.2.1", 95 | "json-socket": "0.3.0", 96 | "tslib": "2.3.1" 97 | }, 98 | "dependencies": { 99 | "tslib": { 100 | "version": "2.3.1", 101 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", 102 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", 103 | "dev": true 104 | } 105 | } 106 | }, 107 | "@nestjs/passport": { 108 | "version": "8.0.1", 109 | "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-8.0.1.tgz", 110 | "integrity": "sha512-vn/ZJLXQKvSf9D0BvEoNFJLfzl9AVqfGtDyQMfWDLbaNpoEB2FyeaHGxdiX6H71oLSrQV78c/yuhfantzwdjdg==", 111 | "dev": true 112 | }, 113 | "@nestjs/swagger": { 114 | "version": "4.8.2", 115 | "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-4.8.2.tgz", 116 | "integrity": "sha512-RSUwcVxrzXF7/b/IZ5lXnYHJ6jIGS9wWRTJKIt1kIaCNWT+0wRfTlAyhQkzs2g35/PTXJEcdIwwY7mBO/bwHzw==", 117 | "dev": true, 118 | "requires": { 119 | "@nestjs/mapped-types": "0.4.1", 120 | "lodash": "4.17.21", 121 | "path-to-regexp": "3.2.0" 122 | }, 123 | "dependencies": { 124 | "@nestjs/mapped-types": { 125 | "version": "0.4.1", 126 | "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-0.4.1.tgz", 127 | "integrity": "sha512-JXrw2LMangSU3vnaXWXVX47GRG1FbbNh4aVBbidDjxT3zlghsoNQY6qyWtT001MCl8lJGo8I6i6+DurBRRxl/Q==", 128 | "dev": true 129 | }, 130 | "lodash": { 131 | "version": "4.17.21", 132 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 133 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 134 | "dev": true 135 | } 136 | } 137 | }, 138 | "@nestjs/testing": { 139 | "version": "8.2.0", 140 | "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-8.2.0.tgz", 141 | "integrity": "sha512-8AIZMuPMZ0zIzuR5iOTZ38zH1CjBhhmwQD/thNhIAANWNYKPEpoqNFzol14hiWWsRUpsV9amgljtTf3hzX6KnA==", 142 | "dev": true, 143 | "requires": { 144 | "optional": "0.1.4", 145 | "tslib": "2.3.1" 146 | }, 147 | "dependencies": { 148 | "tslib": { 149 | "version": "2.3.1", 150 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", 151 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", 152 | "dev": true 153 | } 154 | } 155 | }, 156 | "@nuxtjs/opencollective": { 157 | "version": "0.3.2", 158 | "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", 159 | "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", 160 | "dev": true, 161 | "requires": { 162 | "chalk": "^4.1.0", 163 | "consola": "^2.15.0", 164 | "node-fetch": "^2.6.1" 165 | }, 166 | "dependencies": { 167 | "ansi-styles": { 168 | "version": "4.3.0", 169 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 170 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 171 | "dev": true, 172 | "requires": { 173 | "color-convert": "^2.0.1" 174 | } 175 | }, 176 | "chalk": { 177 | "version": "4.1.2", 178 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 179 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 180 | "dev": true, 181 | "requires": { 182 | "ansi-styles": "^4.1.0", 183 | "supports-color": "^7.1.0" 184 | } 185 | }, 186 | "color-convert": { 187 | "version": "2.0.1", 188 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 189 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 190 | "dev": true, 191 | "requires": { 192 | "color-name": "~1.1.4" 193 | } 194 | }, 195 | "color-name": { 196 | "version": "1.1.4", 197 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 198 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 199 | "dev": true 200 | }, 201 | "has-flag": { 202 | "version": "4.0.0", 203 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 204 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 205 | "dev": true 206 | }, 207 | "supports-color": { 208 | "version": "7.2.0", 209 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 210 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 211 | "dev": true, 212 | "requires": { 213 | "has-flag": "^4.0.0" 214 | } 215 | } 216 | } 217 | }, 218 | "@opentelemetry/semantic-conventions": { 219 | "version": "1.0.1", 220 | "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.0.1.tgz", 221 | "integrity": "sha512-7XU1sfQ8uCVcXLxtAHA8r3qaLJ2oq7sKtEwzZhzuEXqYmjW+n+J4yM3kNo0HQo3Xp1eUe47UM6Wy6yuAvIyllg==", 222 | "dev": true 223 | }, 224 | "@sentry/core": { 225 | "version": "6.17.6", 226 | "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.17.6.tgz", 227 | "integrity": "sha512-wSNsQSqsW8vQ2HEvUEXYOJnzTyVDSWbyH4RHrWV1pQM8zqGx/qfz0sKFM5XFnE9ZeaXKL8LXV3v5i73v+z8lew==", 228 | "dev": true, 229 | "requires": { 230 | "@sentry/hub": "6.17.6", 231 | "@sentry/minimal": "6.17.6", 232 | "@sentry/types": "6.17.6", 233 | "@sentry/utils": "6.17.6", 234 | "tslib": "^1.9.3" 235 | } 236 | }, 237 | "@sentry/hub": { 238 | "version": "6.17.6", 239 | "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.17.6.tgz", 240 | "integrity": "sha512-Ps9nk+DoFia8jhZ1lucdRE0vDx8hqXOsKXJE8a3hK/Ndki0J9jedYqBeLqSgiFG4qRjXpNFcD6TEM6tnQrv5lw==", 241 | "dev": true, 242 | "requires": { 243 | "@sentry/types": "6.17.6", 244 | "@sentry/utils": "6.17.6", 245 | "tslib": "^1.9.3" 246 | } 247 | }, 248 | "@sentry/minimal": { 249 | "version": "6.17.6", 250 | "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.17.6.tgz", 251 | "integrity": "sha512-PLGf8WlhtdHuY6ofwYR3nyClr/TYHHAW6i0r62OZCOXTqnFPJorZpAz3VCCP2jMJmbgVbo03wN+u/xAA/zwObA==", 252 | "dev": true, 253 | "requires": { 254 | "@sentry/hub": "6.17.6", 255 | "@sentry/types": "6.17.6", 256 | "tslib": "^1.9.3" 257 | } 258 | }, 259 | "@sentry/node": { 260 | "version": "6.17.6", 261 | "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.17.6.tgz", 262 | "integrity": "sha512-T1s0yPbGvYpoh9pJgLvpy7s+jVwCyf0ieEoN9rSbnPwbi2vm6MfoV5wtGrE0cBHTPgnyOMv+zq4Q3ww6dfr7Pw==", 263 | "dev": true, 264 | "requires": { 265 | "@sentry/core": "6.17.6", 266 | "@sentry/hub": "6.17.6", 267 | "@sentry/tracing": "6.17.6", 268 | "@sentry/types": "6.17.6", 269 | "@sentry/utils": "6.17.6", 270 | "cookie": "^0.4.1", 271 | "https-proxy-agent": "^5.0.0", 272 | "lru_map": "^0.3.3", 273 | "tslib": "^1.9.3" 274 | }, 275 | "dependencies": { 276 | "cookie": { 277 | "version": "0.4.2", 278 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 279 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 280 | "dev": true 281 | } 282 | } 283 | }, 284 | "@sentry/tracing": { 285 | "version": "6.17.6", 286 | "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.17.6.tgz", 287 | "integrity": "sha512-+h5ov+zEm5WH9+vmFfdT4EIqBOW7Tggzh0BDz8QRStRc2JbvEiSZDs+HlsycBwWMQi/ucJs93FPtNnWjW+xvBw==", 288 | "dev": true, 289 | "requires": { 290 | "@sentry/hub": "6.17.6", 291 | "@sentry/minimal": "6.17.6", 292 | "@sentry/types": "6.17.6", 293 | "@sentry/utils": "6.17.6", 294 | "tslib": "^1.9.3" 295 | } 296 | }, 297 | "@sentry/types": { 298 | "version": "6.17.6", 299 | "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.17.6.tgz", 300 | "integrity": "sha512-peGM873lDJtHd/jwW9Egr/hhxLuF0bcPIf2kMZlvEvW/G5GCbuaCR4ArQJlh7vQyma+NLn/XdojpJkC0TomKrw==", 301 | "dev": true 302 | }, 303 | "@sentry/utils": { 304 | "version": "6.17.6", 305 | "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.17.6.tgz", 306 | "integrity": "sha512-RI797N8Ax5yuKUftVX6dc0XmXqo5CN7XqJYPFzYC8udutQ4L8ZYadtUcqNsdz1ZQxl+rp0XK9Q6wjoWmsI2RXA==", 307 | "dev": true, 308 | "requires": { 309 | "@sentry/types": "6.17.6", 310 | "tslib": "^1.9.3" 311 | } 312 | }, 313 | "@teammaestro/node-common": { 314 | "version": "4.0.0", 315 | "resolved": "https://registry.npmjs.org/@teammaestro/node-common/-/node-common-4.0.0.tgz", 316 | "integrity": "sha512-oGrl16EHO0NxEpxGxWTh16eGfQFLzk/UJ/UfEdWB/qqkNgb3tM+IEAOIACdEhcg9/ipc5L8XQjF/cpx2HuaOoA==", 317 | "dev": true, 318 | "requires": { 319 | "@sentry/node": "^6.3.6", 320 | "config": "^3.2.3", 321 | "deepmerge": "^4.0.0", 322 | "honeycomb-beeline": "^3.2.2", 323 | "log4js": "^6.1.0", 324 | "raven": "^2.6.4" 325 | } 326 | }, 327 | "@types/bluebird": { 328 | "version": "3.5.30", 329 | "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.30.tgz", 330 | "integrity": "sha512-8LhzvcjIoqoi1TghEkRMkbbmM+jhHnBokPGkJWjclMK+Ks0MxEBow3/p2/iFTZ+OIbJHQDSfpgdZEb+af3gfVw==", 331 | "dev": true 332 | }, 333 | "@types/body-parser": { 334 | "version": "1.19.0", 335 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", 336 | "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", 337 | "dev": true, 338 | "requires": { 339 | "@types/connect": "*", 340 | "@types/node": "*" 341 | } 342 | }, 343 | "@types/config": { 344 | "version": "0.0.36", 345 | "resolved": "https://registry.npmjs.org/@types/config/-/config-0.0.36.tgz", 346 | "integrity": "sha512-EoAeT1MyFWh2BJvBDEFInY714bQBbHOAucqxqqhprhbBFqr+B7fuN5T9CJqUIGDzvwubnKKRqmSo6yPo0aSpNw==", 347 | "dev": true 348 | }, 349 | "@types/connect": { 350 | "version": "3.4.33", 351 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", 352 | "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", 353 | "dev": true, 354 | "requires": { 355 | "@types/node": "*" 356 | } 357 | }, 358 | "@types/express": { 359 | "version": "4.17.6", 360 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", 361 | "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==", 362 | "dev": true, 363 | "requires": { 364 | "@types/body-parser": "*", 365 | "@types/express-serve-static-core": "*", 366 | "@types/qs": "*", 367 | "@types/serve-static": "*" 368 | } 369 | }, 370 | "@types/express-serve-static-core": { 371 | "version": "4.17.7", 372 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.7.tgz", 373 | "integrity": "sha512-EMgTj/DF9qpgLXyc+Btimg+XoH7A2liE8uKul8qSmMTHCeNYzydDKFdsJskDvw42UsesCnhO63dO0Grbj8J4Dw==", 374 | "dev": true, 375 | "requires": { 376 | "@types/node": "*", 377 | "@types/qs": "*", 378 | "@types/range-parser": "*" 379 | } 380 | }, 381 | "@types/mime": { 382 | "version": "2.0.1", 383 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", 384 | "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", 385 | "dev": true 386 | }, 387 | "@types/node": { 388 | "version": "12.12.38", 389 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.38.tgz", 390 | "integrity": "sha512-75eLjX0pFuTcUXnnWmALMzzkYorjND0ezNEycaKesbUBg9eGZp4GHPuDmkRc4mQQvIpe29zrzATNRA6hkYqwmA==", 391 | "dev": true 392 | }, 393 | "@types/passport": { 394 | "version": "1.0.3", 395 | "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.3.tgz", 396 | "integrity": "sha512-nyztuxtDPQv9utCzU0qW7Gl8BY2Dn8BKlYAFFyxKipFxjaVd96celbkLCV/tRqqBUZ+JB8If3UfgV8347DTo3Q==", 397 | "dev": true, 398 | "requires": { 399 | "@types/express": "*" 400 | } 401 | }, 402 | "@types/qs": { 403 | "version": "6.9.2", 404 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.2.tgz", 405 | "integrity": "sha512-a9bDi4Z3zCZf4Lv1X/vwnvbbDYSNz59h3i3KdyuYYN+YrLjSeJD0dnphdULDfySvUv6Exy/O0K6wX/kQpnPQ+A==", 406 | "dev": true 407 | }, 408 | "@types/range-parser": { 409 | "version": "1.2.3", 410 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", 411 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", 412 | "dev": true 413 | }, 414 | "@types/raven": { 415 | "version": "2.5.3", 416 | "resolved": "https://registry.npmjs.org/@types/raven/-/raven-2.5.3.tgz", 417 | "integrity": "sha512-k6vxiX5I6/GEqJS9mMYPVIgMJf/X26n09NfzuqBpdcEp684RIwpdrwCgSyJGuy8EaSG1wc2rFP7xVEAixPzw7Q==", 418 | "dev": true, 419 | "requires": { 420 | "@types/node": "*" 421 | } 422 | }, 423 | "@types/redis": { 424 | "version": "2.8.20", 425 | "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.20.tgz", 426 | "integrity": "sha512-IJOvkg+BnCRDlMrrBIasCs8bqddnjiplI26HwsPSeY/biEJaDzAQnjs2i5z5vJelTbi3TIiLUIrKXk7j9Tmi/Q==", 427 | "dev": true, 428 | "requires": { 429 | "@types/node": "*" 430 | } 431 | }, 432 | "@types/serve-static": { 433 | "version": "1.13.3", 434 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", 435 | "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", 436 | "dev": true, 437 | "requires": { 438 | "@types/express-serve-static-core": "*", 439 | "@types/mime": "*" 440 | } 441 | }, 442 | "@types/validator": { 443 | "version": "13.0.0", 444 | "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.0.0.tgz", 445 | "integrity": "sha512-WAy5txG7aFX8Vw3sloEKp5p/t/Xt8jD3GRD9DacnFv6Vo8ubudAsRTXgxpQwU0mpzY/H8U4db3roDuCMjShBmw==", 446 | "dev": true 447 | }, 448 | "acorn": { 449 | "version": "8.7.0", 450 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", 451 | "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", 452 | "dev": true 453 | }, 454 | "acorn-walk": { 455 | "version": "8.2.0", 456 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 457 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 458 | "dev": true 459 | }, 460 | "agent-base": { 461 | "version": "6.0.2", 462 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 463 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 464 | "dev": true, 465 | "requires": { 466 | "debug": "4" 467 | } 468 | }, 469 | "ansi-styles": { 470 | "version": "3.2.1", 471 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 472 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 473 | "dev": true, 474 | "requires": { 475 | "color-convert": "^1.9.0" 476 | } 477 | }, 478 | "argparse": { 479 | "version": "1.0.10", 480 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 481 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 482 | "dev": true, 483 | "requires": { 484 | "sprintf-js": "~1.0.2" 485 | } 486 | }, 487 | "array-flatten": { 488 | "version": "3.0.0", 489 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", 490 | "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==", 491 | "dev": true 492 | }, 493 | "axios": { 494 | "version": "0.24.0", 495 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", 496 | "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", 497 | "dev": true, 498 | "requires": { 499 | "follow-redirects": "^1.14.4" 500 | } 501 | }, 502 | "balanced-match": { 503 | "version": "1.0.0", 504 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 505 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 506 | "dev": true 507 | }, 508 | "bluebird": { 509 | "version": "3.7.2", 510 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", 511 | "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" 512 | }, 513 | "brace-expansion": { 514 | "version": "1.1.11", 515 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 516 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 517 | "dev": true, 518 | "requires": { 519 | "balanced-match": "^1.0.0", 520 | "concat-map": "0.0.1" 521 | } 522 | }, 523 | "builtin-modules": { 524 | "version": "1.1.1", 525 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 526 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 527 | "dev": true 528 | }, 529 | "bytes": { 530 | "version": "3.1.1", 531 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", 532 | "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", 533 | "dev": true 534 | }, 535 | "call-bind": { 536 | "version": "1.0.2", 537 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 538 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 539 | "dev": true, 540 | "requires": { 541 | "function-bind": "^1.1.1", 542 | "get-intrinsic": "^1.0.2" 543 | } 544 | }, 545 | "chalk": { 546 | "version": "2.4.2", 547 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 548 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 549 | "dev": true, 550 | "requires": { 551 | "ansi-styles": "^3.2.1", 552 | "escape-string-regexp": "^1.0.5", 553 | "supports-color": "^5.3.0" 554 | } 555 | }, 556 | "charenc": { 557 | "version": "0.0.2", 558 | "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", 559 | "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" 560 | }, 561 | "class-validator": { 562 | "version": "0.12.2", 563 | "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.12.2.tgz", 564 | "integrity": "sha512-TDzPzp8BmpsbPhQpccB3jMUE/3pK0TyqamrK0kcx+ZeFytMA+O6q87JZZGObHHnoo9GM8vl/JppIyKWeEA/EVw==", 565 | "dev": true, 566 | "requires": { 567 | "@types/validator": "13.0.0", 568 | "google-libphonenumber": "^3.2.8", 569 | "tslib": ">=1.9.0", 570 | "validator": "13.0.0" 571 | } 572 | }, 573 | "color-convert": { 574 | "version": "1.9.3", 575 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 576 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 577 | "dev": true, 578 | "requires": { 579 | "color-name": "1.1.3" 580 | } 581 | }, 582 | "color-name": { 583 | "version": "1.1.3", 584 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 585 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 586 | "dev": true 587 | }, 588 | "commander": { 589 | "version": "2.20.3", 590 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 591 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 592 | "dev": true 593 | }, 594 | "concat-map": { 595 | "version": "0.0.1", 596 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 597 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 598 | "dev": true 599 | }, 600 | "config": { 601 | "version": "3.3.1", 602 | "resolved": "https://registry.npmjs.org/config/-/config-3.3.1.tgz", 603 | "integrity": "sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q==", 604 | "dev": true, 605 | "requires": { 606 | "json5": "^2.1.1" 607 | } 608 | }, 609 | "consola": { 610 | "version": "2.15.3", 611 | "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", 612 | "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", 613 | "dev": true 614 | }, 615 | "cookie": { 616 | "version": "0.3.1", 617 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 618 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 619 | }, 620 | "core-util-is": { 621 | "version": "1.0.3", 622 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 623 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", 624 | "dev": true 625 | }, 626 | "crypt": { 627 | "version": "0.0.2", 628 | "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", 629 | "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" 630 | }, 631 | "data-uri-to-buffer": { 632 | "version": "3.0.1", 633 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", 634 | "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", 635 | "dev": true 636 | }, 637 | "date-format": { 638 | "version": "3.0.0", 639 | "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", 640 | "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", 641 | "dev": true 642 | }, 643 | "debug": { 644 | "version": "4.1.1", 645 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 646 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 647 | "dev": true, 648 | "requires": { 649 | "ms": "^2.1.1" 650 | } 651 | }, 652 | "deep-is": { 653 | "version": "0.1.4", 654 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 655 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 656 | "dev": true 657 | }, 658 | "deepmerge": { 659 | "version": "4.2.2", 660 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", 661 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", 662 | "dev": true 663 | }, 664 | "delayed-stream": { 665 | "version": "1.0.0", 666 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 667 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 668 | "dev": true 669 | }, 670 | "depd": { 671 | "version": "1.1.2", 672 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 673 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 674 | "dev": true 675 | }, 676 | "diff": { 677 | "version": "4.0.2", 678 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 679 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 680 | "dev": true 681 | }, 682 | "double-ended-queue": { 683 | "version": "2.1.0-0", 684 | "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", 685 | "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" 686 | }, 687 | "escape-string-regexp": { 688 | "version": "1.0.5", 689 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 690 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 691 | "dev": true 692 | }, 693 | "esprima": { 694 | "version": "4.0.1", 695 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 696 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 697 | "dev": true 698 | }, 699 | "estraverse": { 700 | "version": "4.3.0", 701 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 702 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 703 | "dev": true 704 | }, 705 | "esutils": { 706 | "version": "2.0.3", 707 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 708 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 709 | "dev": true 710 | }, 711 | "extend": { 712 | "version": "2.0.2", 713 | "resolved": "https://registry.npmjs.org/extend/-/extend-2.0.2.tgz", 714 | "integrity": "sha512-AgFD4VU+lVLP6vjnlNfF7OeInLTyeyckCNPEsuxz1vi786UuK/nk6ynPuhn/h+Ju9++TQyr5EpLRI14fc1QtTQ==", 715 | "dev": true 716 | }, 717 | "fast-levenshtein": { 718 | "version": "2.0.6", 719 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 720 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 721 | "dev": true 722 | }, 723 | "fast-safe-stringify": { 724 | "version": "2.1.1", 725 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", 726 | "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", 727 | "dev": true 728 | }, 729 | "file-uri-to-path": { 730 | "version": "2.0.0", 731 | "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", 732 | "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", 733 | "dev": true 734 | }, 735 | "flatted": { 736 | "version": "2.0.2", 737 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", 738 | "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", 739 | "dev": true 740 | }, 741 | "follow-redirects": { 742 | "version": "1.14.5", 743 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", 744 | "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", 745 | "dev": true 746 | }, 747 | "fs-extra": { 748 | "version": "8.1.0", 749 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", 750 | "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", 751 | "dev": true, 752 | "requires": { 753 | "graceful-fs": "^4.2.0", 754 | "jsonfile": "^4.0.0", 755 | "universalify": "^0.1.0" 756 | } 757 | }, 758 | "fs.realpath": { 759 | "version": "1.0.0", 760 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 761 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 762 | "dev": true 763 | }, 764 | "ftp": { 765 | "version": "0.3.10", 766 | "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", 767 | "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", 768 | "dev": true, 769 | "requires": { 770 | "readable-stream": "1.1.x", 771 | "xregexp": "2.0.0" 772 | } 773 | }, 774 | "function-bind": { 775 | "version": "1.1.1", 776 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 777 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 778 | "dev": true 779 | }, 780 | "get-intrinsic": { 781 | "version": "1.1.1", 782 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", 783 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", 784 | "dev": true, 785 | "requires": { 786 | "function-bind": "^1.1.1", 787 | "has": "^1.0.3", 788 | "has-symbols": "^1.0.1" 789 | } 790 | }, 791 | "glob": { 792 | "version": "7.1.6", 793 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 794 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 795 | "dev": true, 796 | "requires": { 797 | "fs.realpath": "^1.0.0", 798 | "inflight": "^1.0.4", 799 | "inherits": "2", 800 | "minimatch": "^3.0.4", 801 | "once": "^1.3.0", 802 | "path-is-absolute": "^1.0.0" 803 | } 804 | }, 805 | "google-libphonenumber": { 806 | "version": "3.2.9", 807 | "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.9.tgz", 808 | "integrity": "sha512-PCU6Z5drRaFHNICJcurXsf6DN//ZNl0PXmPIpbHi+E1pp54GwyrhxBn57rr1nx+3mNUOMr4zeWirIKPc4ziJgw==", 809 | "dev": true 810 | }, 811 | "graceful-fs": { 812 | "version": "4.2.4", 813 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 814 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", 815 | "dev": true 816 | }, 817 | "has": { 818 | "version": "1.0.3", 819 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 820 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 821 | "dev": true, 822 | "requires": { 823 | "function-bind": "^1.1.1" 824 | } 825 | }, 826 | "has-flag": { 827 | "version": "3.0.0", 828 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 829 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 830 | "dev": true 831 | }, 832 | "has-symbols": { 833 | "version": "1.0.2", 834 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", 835 | "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", 836 | "dev": true 837 | }, 838 | "honeycomb-beeline": { 839 | "version": "3.2.2", 840 | "resolved": "https://registry.npmjs.org/honeycomb-beeline/-/honeycomb-beeline-3.2.2.tgz", 841 | "integrity": "sha512-5/pxB07UBODt5BC5Q6e7l9Q7A5MBhXr0EMRwrD5CA0rLicyhofbJtdDBX2ZAkDSl8i9WgPObAe/GfRBDT7mY2Q==", 842 | "dev": true, 843 | "requires": { 844 | "@opentelemetry/api": "^1.0.2", 845 | "@opentelemetry/core": "^1.0.0", 846 | "array-flatten": "^3.0.0", 847 | "debug": "^4.2.0", 848 | "libhoney": "^3.0.0", 849 | "on-headers": "^1.0.2", 850 | "shimmer": "^1.2.1" 851 | }, 852 | "dependencies": { 853 | "@opentelemetry/api": { 854 | "version": "1.0.4", 855 | "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.4.tgz", 856 | "integrity": "sha512-BuJuXRSJNQ3QoKA6GWWDyuLpOUck+9hAXNMCnrloc1aWVoy6Xq6t9PUV08aBZ4Lutqq2LEHM486bpZqoViScog==", 857 | "dev": true 858 | }, 859 | "@opentelemetry/core": { 860 | "version": "1.0.1", 861 | "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.0.1.tgz", 862 | "integrity": "sha512-90nQ2X6b/8X+xjcLDBYKooAcOsIlwLRYm+1VsxcX5cHl6V4CSVmDpBreQSDH/A21SqROzapk6813008SatmPpQ==", 863 | "dev": true, 864 | "requires": { 865 | "@opentelemetry/semantic-conventions": "1.0.1" 866 | } 867 | }, 868 | "debug": { 869 | "version": "4.3.3", 870 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 871 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 872 | "dev": true, 873 | "requires": { 874 | "ms": "2.1.2" 875 | } 876 | }, 877 | "degenerator": { 878 | "version": "3.0.1", 879 | "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.1.tgz", 880 | "integrity": "sha512-LFsIFEeLPlKvAKXu7j3ssIG6RT0TbI7/GhsqrI0DnHASEQjXQ0LUSYcjJteGgRGmZbl1TnMSxpNQIAiJ7Du5TQ==", 881 | "dev": true, 882 | "requires": { 883 | "ast-types": "^0.13.2", 884 | "escodegen": "^1.8.1", 885 | "esprima": "^4.0.0", 886 | "vm2": "^3.9.3" 887 | }, 888 | "dependencies": { 889 | "ast-types": { 890 | "version": "0.13.4", 891 | "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", 892 | "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", 893 | "dev": true, 894 | "requires": { 895 | "tslib": "^2.0.1" 896 | } 897 | }, 898 | "escodegen": { 899 | "version": "1.14.3", 900 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", 901 | "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", 902 | "dev": true, 903 | "requires": { 904 | "esprima": "^4.0.1", 905 | "estraverse": "^4.2.0", 906 | "esutils": "^2.0.2", 907 | "optionator": "^0.8.1", 908 | "source-map": "~0.6.1" 909 | } 910 | } 911 | } 912 | }, 913 | "form-data": { 914 | "version": "3.0.1", 915 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", 916 | "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", 917 | "dev": true, 918 | "requires": { 919 | "asynckit": "^0.4.0", 920 | "combined-stream": "^1.0.8", 921 | "mime-types": "^2.1.12" 922 | }, 923 | "dependencies": { 924 | "asynckit": { 925 | "version": "0.4.0", 926 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 927 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 928 | "dev": true 929 | }, 930 | "combined-stream": { 931 | "version": "1.0.8", 932 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 933 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 934 | "dev": true, 935 | "requires": { 936 | "delayed-stream": "~1.0.0" 937 | } 938 | }, 939 | "mime-types": { 940 | "version": "2.1.34", 941 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", 942 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", 943 | "dev": true, 944 | "requires": { 945 | "mime-db": "1.51.0" 946 | } 947 | } 948 | } 949 | }, 950 | "libhoney": { 951 | "version": "3.0.0", 952 | "resolved": "https://registry.npmjs.org/libhoney/-/libhoney-3.0.0.tgz", 953 | "integrity": "sha512-aAvZblw2ad2iRXrLNk/CnOgQG2Zpt4uuCcVjeC+n/GMsMQRKOGxZ7whI7N4b0oa2aFjsNUQgfwv+xqVMUnO+Mg==", 954 | "dev": true, 955 | "requires": { 956 | "superagent": "^6.1.0", 957 | "superagent-proxy": "^3.0.0", 958 | "urljoin": "^0.1.5" 959 | }, 960 | "dependencies": { 961 | "urljoin": { 962 | "version": "0.1.5", 963 | "resolved": "https://registry.npmjs.org/urljoin/-/urljoin-0.1.5.tgz", 964 | "integrity": "sha1-sl0sYRLFWsnVAJakmg8ft/T1OSE=", 965 | "dev": true, 966 | "requires": { 967 | "extend": "~2.0.0" 968 | } 969 | } 970 | } 971 | }, 972 | "mime": { 973 | "version": "2.6.0", 974 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", 975 | "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", 976 | "dev": true 977 | }, 978 | "pac-proxy-agent": { 979 | "version": "5.0.0", 980 | "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", 981 | "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", 982 | "dev": true, 983 | "requires": { 984 | "@tootallnate/once": "1", 985 | "agent-base": "6", 986 | "debug": "4", 987 | "get-uri": "3", 988 | "http-proxy-agent": "^4.0.1", 989 | "https-proxy-agent": "5", 990 | "pac-resolver": "^5.0.0", 991 | "raw-body": "^2.2.0", 992 | "socks-proxy-agent": "5" 993 | }, 994 | "dependencies": { 995 | "@tootallnate/once": { 996 | "version": "1.1.2", 997 | "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", 998 | "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", 999 | "dev": true 1000 | }, 1001 | "get-uri": { 1002 | "version": "3.0.2", 1003 | "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", 1004 | "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", 1005 | "dev": true, 1006 | "requires": { 1007 | "@tootallnate/once": "1", 1008 | "data-uri-to-buffer": "3", 1009 | "debug": "4", 1010 | "file-uri-to-path": "2", 1011 | "fs-extra": "^8.1.0", 1012 | "ftp": "^0.3.10" 1013 | } 1014 | }, 1015 | "http-proxy-agent": { 1016 | "version": "4.0.1", 1017 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", 1018 | "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", 1019 | "dev": true, 1020 | "requires": { 1021 | "@tootallnate/once": "1", 1022 | "agent-base": "6", 1023 | "debug": "4" 1024 | } 1025 | }, 1026 | "raw-body": { 1027 | "version": "2.4.2", 1028 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", 1029 | "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", 1030 | "dev": true, 1031 | "requires": { 1032 | "bytes": "3.1.1", 1033 | "http-errors": "1.8.1", 1034 | "iconv-lite": "0.4.24", 1035 | "unpipe": "1.0.0" 1036 | } 1037 | }, 1038 | "socks-proxy-agent": { 1039 | "version": "5.0.1", 1040 | "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", 1041 | "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", 1042 | "dev": true, 1043 | "requires": { 1044 | "agent-base": "^6.0.2", 1045 | "debug": "4", 1046 | "socks": "^2.3.3" 1047 | } 1048 | } 1049 | } 1050 | }, 1051 | "pac-resolver": { 1052 | "version": "5.0.0", 1053 | "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.0.tgz", 1054 | "integrity": "sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA==", 1055 | "dev": true, 1056 | "requires": { 1057 | "degenerator": "^3.0.1", 1058 | "ip": "^1.1.5", 1059 | "netmask": "^2.0.1" 1060 | }, 1061 | "dependencies": { 1062 | "ip": { 1063 | "version": "1.1.5", 1064 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 1065 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", 1066 | "dev": true 1067 | }, 1068 | "netmask": { 1069 | "version": "2.0.2", 1070 | "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", 1071 | "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", 1072 | "dev": true 1073 | } 1074 | } 1075 | }, 1076 | "proxy-agent": { 1077 | "version": "5.0.0", 1078 | "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", 1079 | "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", 1080 | "dev": true, 1081 | "requires": { 1082 | "agent-base": "^6.0.0", 1083 | "debug": "4", 1084 | "http-proxy-agent": "^4.0.0", 1085 | "https-proxy-agent": "^5.0.0", 1086 | "lru-cache": "^5.1.1", 1087 | "pac-proxy-agent": "^5.0.0", 1088 | "proxy-from-env": "^1.0.0", 1089 | "socks-proxy-agent": "^5.0.0" 1090 | }, 1091 | "dependencies": { 1092 | "@tootallnate/once": { 1093 | "version": "1.1.2", 1094 | "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", 1095 | "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", 1096 | "dev": true 1097 | }, 1098 | "http-proxy-agent": { 1099 | "version": "4.0.1", 1100 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", 1101 | "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", 1102 | "dev": true, 1103 | "requires": { 1104 | "@tootallnate/once": "1", 1105 | "agent-base": "6", 1106 | "debug": "4" 1107 | } 1108 | }, 1109 | "lru-cache": { 1110 | "version": "5.1.1", 1111 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 1112 | "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 1113 | "dev": true, 1114 | "requires": { 1115 | "yallist": "^3.0.2" 1116 | } 1117 | }, 1118 | "proxy-from-env": { 1119 | "version": "1.1.0", 1120 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 1121 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 1122 | "dev": true 1123 | }, 1124 | "socks-proxy-agent": { 1125 | "version": "5.0.1", 1126 | "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", 1127 | "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", 1128 | "dev": true, 1129 | "requires": { 1130 | "agent-base": "^6.0.2", 1131 | "debug": "4", 1132 | "socks": "^2.3.3" 1133 | } 1134 | } 1135 | } 1136 | }, 1137 | "readable-stream": { 1138 | "version": "3.6.0", 1139 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1140 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1141 | "dev": true, 1142 | "requires": { 1143 | "inherits": "^2.0.3", 1144 | "string_decoder": "^1.1.1", 1145 | "util-deprecate": "^1.0.1" 1146 | }, 1147 | "dependencies": { 1148 | "string_decoder": { 1149 | "version": "1.3.0", 1150 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1151 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1152 | "dev": true, 1153 | "requires": { 1154 | "safe-buffer": "~5.2.0" 1155 | } 1156 | }, 1157 | "util-deprecate": { 1158 | "version": "1.0.2", 1159 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1160 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 1161 | "dev": true 1162 | } 1163 | } 1164 | }, 1165 | "semver": { 1166 | "version": "7.3.5", 1167 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 1168 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 1169 | "dev": true, 1170 | "requires": { 1171 | "lru-cache": "^6.0.0" 1172 | }, 1173 | "dependencies": { 1174 | "lru-cache": { 1175 | "version": "6.0.0", 1176 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1177 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1178 | "dev": true, 1179 | "requires": { 1180 | "yallist": "^4.0.0" 1181 | } 1182 | }, 1183 | "yallist": { 1184 | "version": "4.0.0", 1185 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1186 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 1187 | "dev": true 1188 | } 1189 | } 1190 | }, 1191 | "superagent": { 1192 | "version": "6.1.0", 1193 | "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", 1194 | "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", 1195 | "dev": true, 1196 | "requires": { 1197 | "component-emitter": "^1.3.0", 1198 | "cookiejar": "^2.1.2", 1199 | "debug": "^4.1.1", 1200 | "fast-safe-stringify": "^2.0.7", 1201 | "form-data": "^3.0.0", 1202 | "formidable": "^1.2.2", 1203 | "methods": "^1.1.2", 1204 | "mime": "^2.4.6", 1205 | "qs": "^6.9.4", 1206 | "readable-stream": "^3.6.0", 1207 | "semver": "^7.3.2" 1208 | }, 1209 | "dependencies": { 1210 | "component-emitter": { 1211 | "version": "1.3.0", 1212 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", 1213 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", 1214 | "dev": true 1215 | }, 1216 | "cookiejar": { 1217 | "version": "2.1.3", 1218 | "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", 1219 | "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", 1220 | "dev": true 1221 | }, 1222 | "formidable": { 1223 | "version": "1.2.6", 1224 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", 1225 | "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", 1226 | "dev": true 1227 | }, 1228 | "methods": { 1229 | "version": "1.1.2", 1230 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1231 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 1232 | "dev": true 1233 | }, 1234 | "qs": { 1235 | "version": "6.10.3", 1236 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", 1237 | "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", 1238 | "dev": true, 1239 | "requires": { 1240 | "side-channel": "^1.0.4" 1241 | } 1242 | } 1243 | } 1244 | }, 1245 | "superagent-proxy": { 1246 | "version": "3.0.0", 1247 | "resolved": "https://registry.npmjs.org/superagent-proxy/-/superagent-proxy-3.0.0.tgz", 1248 | "integrity": "sha512-wAlRInOeDFyd9pyonrkJspdRAxdLrcsZ6aSnS+8+nu4x1aXbz6FWSTT9M6Ibze+eG60szlL7JA8wEIV7bPWuyQ==", 1249 | "dev": true, 1250 | "requires": { 1251 | "debug": "^4.3.2", 1252 | "proxy-agent": "^5.0.0" 1253 | } 1254 | }, 1255 | "tslib": { 1256 | "version": "2.3.1", 1257 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", 1258 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", 1259 | "dev": true 1260 | }, 1261 | "yallist": { 1262 | "version": "3.1.1", 1263 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 1264 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", 1265 | "dev": true 1266 | } 1267 | } 1268 | }, 1269 | "http-errors": { 1270 | "version": "1.8.1", 1271 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", 1272 | "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", 1273 | "dev": true, 1274 | "requires": { 1275 | "depd": "~1.1.2", 1276 | "inherits": "2.0.4", 1277 | "setprototypeof": "1.2.0", 1278 | "statuses": ">= 1.5.0 < 2", 1279 | "toidentifier": "1.0.1" 1280 | } 1281 | }, 1282 | "https-proxy-agent": { 1283 | "version": "5.0.0", 1284 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", 1285 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", 1286 | "dev": true, 1287 | "requires": { 1288 | "agent-base": "6", 1289 | "debug": "4" 1290 | } 1291 | }, 1292 | "iconv-lite": { 1293 | "version": "0.4.24", 1294 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1295 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1296 | "dev": true, 1297 | "requires": { 1298 | "safer-buffer": ">= 2.1.2 < 3" 1299 | } 1300 | }, 1301 | "inflight": { 1302 | "version": "1.0.6", 1303 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1304 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1305 | "dev": true, 1306 | "requires": { 1307 | "once": "^1.3.0", 1308 | "wrappy": "1" 1309 | } 1310 | }, 1311 | "inherits": { 1312 | "version": "2.0.4", 1313 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1314 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1315 | "dev": true 1316 | }, 1317 | "ip": { 1318 | "version": "1.1.5", 1319 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 1320 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", 1321 | "dev": true 1322 | }, 1323 | "is-buffer": { 1324 | "version": "1.1.6", 1325 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 1326 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 1327 | }, 1328 | "isarray": { 1329 | "version": "0.0.1", 1330 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1331 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 1332 | "dev": true 1333 | }, 1334 | "iterare": { 1335 | "version": "1.2.1", 1336 | "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", 1337 | "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", 1338 | "dev": true 1339 | }, 1340 | "js-tokens": { 1341 | "version": "4.0.0", 1342 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1343 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1344 | "dev": true 1345 | }, 1346 | "js-yaml": { 1347 | "version": "3.13.1", 1348 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 1349 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 1350 | "dev": true, 1351 | "requires": { 1352 | "argparse": "^1.0.7", 1353 | "esprima": "^4.0.0" 1354 | } 1355 | }, 1356 | "json-socket": { 1357 | "version": "0.3.0", 1358 | "resolved": "https://registry.npmjs.org/json-socket/-/json-socket-0.3.0.tgz", 1359 | "integrity": "sha512-jc8ZbUnYIWdxERFWQKVgwSLkGSe+kyzvmYxwNaRgx/c8NNyuHes4UHnPM3LUrAFXUx1BhNJ94n1h/KCRlbvV0g==", 1360 | "dev": true 1361 | }, 1362 | "json5": { 1363 | "version": "2.1.3", 1364 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", 1365 | "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", 1366 | "dev": true, 1367 | "requires": { 1368 | "minimist": "^1.2.5" 1369 | } 1370 | }, 1371 | "jsonfile": { 1372 | "version": "4.0.0", 1373 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 1374 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 1375 | "dev": true, 1376 | "requires": { 1377 | "graceful-fs": "^4.1.6" 1378 | } 1379 | }, 1380 | "levn": { 1381 | "version": "0.3.0", 1382 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 1383 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 1384 | "dev": true, 1385 | "requires": { 1386 | "prelude-ls": "~1.1.2", 1387 | "type-check": "~0.3.2" 1388 | } 1389 | }, 1390 | "log4js": { 1391 | "version": "6.2.1", 1392 | "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.2.1.tgz", 1393 | "integrity": "sha512-7n+Oqxxz7VcQJhIlqhcYZBTpbcQ7XsR0MUIfJkx/n3VUjkAS4iUr+4UJlhxf28RvP9PMGQXbgTUhLApnu0XXgA==", 1394 | "dev": true, 1395 | "requires": { 1396 | "date-format": "^3.0.0", 1397 | "debug": "^4.1.1", 1398 | "flatted": "^2.0.1", 1399 | "rfdc": "^1.1.4", 1400 | "streamroller": "^2.2.4" 1401 | } 1402 | }, 1403 | "lru_map": { 1404 | "version": "0.3.3", 1405 | "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", 1406 | "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=", 1407 | "dev": true 1408 | }, 1409 | "md5": { 1410 | "version": "2.2.1", 1411 | "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", 1412 | "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", 1413 | "requires": { 1414 | "charenc": "~0.0.1", 1415 | "crypt": "~0.0.1", 1416 | "is-buffer": "~1.1.1" 1417 | } 1418 | }, 1419 | "mime-db": { 1420 | "version": "1.51.0", 1421 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", 1422 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", 1423 | "dev": true 1424 | }, 1425 | "minimatch": { 1426 | "version": "3.0.4", 1427 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1428 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1429 | "dev": true, 1430 | "requires": { 1431 | "brace-expansion": "^1.1.7" 1432 | } 1433 | }, 1434 | "minimist": { 1435 | "version": "1.2.5", 1436 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 1437 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 1438 | "dev": true 1439 | }, 1440 | "mkdirp": { 1441 | "version": "0.5.5", 1442 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 1443 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 1444 | "dev": true, 1445 | "requires": { 1446 | "minimist": "^1.2.5" 1447 | } 1448 | }, 1449 | "ms": { 1450 | "version": "2.1.2", 1451 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1452 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1453 | "dev": true 1454 | }, 1455 | "node-fetch": { 1456 | "version": "2.6.6", 1457 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", 1458 | "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", 1459 | "dev": true, 1460 | "requires": { 1461 | "whatwg-url": "^5.0.0" 1462 | } 1463 | }, 1464 | "object-hash": { 1465 | "version": "2.2.0", 1466 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", 1467 | "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", 1468 | "dev": true 1469 | }, 1470 | "object-inspect": { 1471 | "version": "1.12.0", 1472 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", 1473 | "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", 1474 | "dev": true 1475 | }, 1476 | "on-headers": { 1477 | "version": "1.0.2", 1478 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 1479 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", 1480 | "dev": true 1481 | }, 1482 | "once": { 1483 | "version": "1.4.0", 1484 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1485 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1486 | "dev": true, 1487 | "requires": { 1488 | "wrappy": "1" 1489 | } 1490 | }, 1491 | "optional": { 1492 | "version": "0.1.4", 1493 | "resolved": "https://registry.npmjs.org/optional/-/optional-0.1.4.tgz", 1494 | "integrity": "sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==", 1495 | "dev": true 1496 | }, 1497 | "optionator": { 1498 | "version": "0.8.3", 1499 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", 1500 | "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", 1501 | "dev": true, 1502 | "requires": { 1503 | "deep-is": "~0.1.3", 1504 | "fast-levenshtein": "~2.0.6", 1505 | "levn": "~0.3.0", 1506 | "prelude-ls": "~1.1.2", 1507 | "type-check": "~0.3.2", 1508 | "word-wrap": "~1.2.3" 1509 | } 1510 | }, 1511 | "passport": { 1512 | "version": "0.4.1", 1513 | "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", 1514 | "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", 1515 | "dev": true, 1516 | "requires": { 1517 | "passport-strategy": "1.x.x", 1518 | "pause": "0.0.1" 1519 | } 1520 | }, 1521 | "passport-strategy": { 1522 | "version": "1.0.0", 1523 | "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", 1524 | "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", 1525 | "dev": true 1526 | }, 1527 | "path-is-absolute": { 1528 | "version": "1.0.1", 1529 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1530 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1531 | "dev": true 1532 | }, 1533 | "path-parse": { 1534 | "version": "1.0.6", 1535 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1536 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 1537 | "dev": true 1538 | }, 1539 | "path-to-regexp": { 1540 | "version": "3.2.0", 1541 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", 1542 | "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==", 1543 | "dev": true 1544 | }, 1545 | "pause": { 1546 | "version": "0.0.1", 1547 | "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", 1548 | "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=", 1549 | "dev": true 1550 | }, 1551 | "prelude-ls": { 1552 | "version": "1.1.2", 1553 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 1554 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 1555 | "dev": true 1556 | }, 1557 | "raven": { 1558 | "version": "2.6.4", 1559 | "resolved": "https://registry.npmjs.org/raven/-/raven-2.6.4.tgz", 1560 | "integrity": "sha512-6PQdfC4+DQSFncowthLf+B6Hr0JpPsFBgTVYTAOq7tCmx/kR4SXbeawtPch20+3QfUcQDoJBLjWW1ybvZ4kXTw==", 1561 | "requires": { 1562 | "cookie": "0.3.1", 1563 | "md5": "^2.2.1", 1564 | "stack-trace": "0.0.10", 1565 | "timed-out": "4.0.1", 1566 | "uuid": "3.3.2" 1567 | } 1568 | }, 1569 | "readable-stream": { 1570 | "version": "1.1.14", 1571 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 1572 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 1573 | "dev": true, 1574 | "requires": { 1575 | "core-util-is": "~1.0.0", 1576 | "inherits": "~2.0.1", 1577 | "isarray": "0.0.1", 1578 | "string_decoder": "~0.10.x" 1579 | } 1580 | }, 1581 | "redis": { 1582 | "version": "2.8.0", 1583 | "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", 1584 | "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", 1585 | "requires": { 1586 | "double-ended-queue": "^2.1.0-0", 1587 | "redis-commands": "^1.2.0", 1588 | "redis-parser": "^2.6.0" 1589 | } 1590 | }, 1591 | "redis-commands": { 1592 | "version": "1.5.0", 1593 | "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", 1594 | "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" 1595 | }, 1596 | "redis-parser": { 1597 | "version": "2.6.0", 1598 | "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", 1599 | "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" 1600 | }, 1601 | "reflect-metadata": { 1602 | "version": "0.1.13", 1603 | "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", 1604 | "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", 1605 | "dev": true 1606 | }, 1607 | "resolve": { 1608 | "version": "1.17.0", 1609 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 1610 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 1611 | "dev": true, 1612 | "requires": { 1613 | "path-parse": "^1.0.6" 1614 | } 1615 | }, 1616 | "rfdc": { 1617 | "version": "1.1.4", 1618 | "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", 1619 | "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", 1620 | "dev": true 1621 | }, 1622 | "rxjs": { 1623 | "version": "6.5.5", 1624 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", 1625 | "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", 1626 | "dev": true, 1627 | "requires": { 1628 | "tslib": "^1.9.0" 1629 | } 1630 | }, 1631 | "safe-buffer": { 1632 | "version": "5.2.1", 1633 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1634 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1635 | "dev": true 1636 | }, 1637 | "safer-buffer": { 1638 | "version": "2.1.2", 1639 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1640 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1641 | "dev": true 1642 | }, 1643 | "semver": { 1644 | "version": "5.7.1", 1645 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1646 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 1647 | "dev": true 1648 | }, 1649 | "setprototypeof": { 1650 | "version": "1.2.0", 1651 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1652 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 1653 | "dev": true 1654 | }, 1655 | "shimmer": { 1656 | "version": "1.2.1", 1657 | "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", 1658 | "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", 1659 | "dev": true 1660 | }, 1661 | "side-channel": { 1662 | "version": "1.0.4", 1663 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1664 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1665 | "dev": true, 1666 | "requires": { 1667 | "call-bind": "^1.0.0", 1668 | "get-intrinsic": "^1.0.2", 1669 | "object-inspect": "^1.9.0" 1670 | } 1671 | }, 1672 | "smart-buffer": { 1673 | "version": "4.2.0", 1674 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 1675 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", 1676 | "dev": true 1677 | }, 1678 | "socks": { 1679 | "version": "2.6.2", 1680 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", 1681 | "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", 1682 | "dev": true, 1683 | "requires": { 1684 | "ip": "^1.1.5", 1685 | "smart-buffer": "^4.2.0" 1686 | } 1687 | }, 1688 | "source-map": { 1689 | "version": "0.6.1", 1690 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1691 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1692 | "dev": true, 1693 | "optional": true 1694 | }, 1695 | "sprintf-js": { 1696 | "version": "1.0.3", 1697 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1698 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1699 | "dev": true 1700 | }, 1701 | "stack-trace": { 1702 | "version": "0.0.10", 1703 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1704 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" 1705 | }, 1706 | "statuses": { 1707 | "version": "1.5.0", 1708 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1709 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 1710 | "dev": true 1711 | }, 1712 | "streamroller": { 1713 | "version": "2.2.4", 1714 | "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", 1715 | "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", 1716 | "dev": true, 1717 | "requires": { 1718 | "date-format": "^2.1.0", 1719 | "debug": "^4.1.1", 1720 | "fs-extra": "^8.1.0" 1721 | }, 1722 | "dependencies": { 1723 | "date-format": { 1724 | "version": "2.1.0", 1725 | "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", 1726 | "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", 1727 | "dev": true 1728 | } 1729 | } 1730 | }, 1731 | "string_decoder": { 1732 | "version": "0.10.31", 1733 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1734 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 1735 | "dev": true 1736 | }, 1737 | "supports-color": { 1738 | "version": "5.5.0", 1739 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1740 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1741 | "dev": true, 1742 | "requires": { 1743 | "has-flag": "^3.0.0" 1744 | } 1745 | }, 1746 | "timed-out": { 1747 | "version": "4.0.1", 1748 | "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", 1749 | "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" 1750 | }, 1751 | "toidentifier": { 1752 | "version": "1.0.1", 1753 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1754 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1755 | "dev": true 1756 | }, 1757 | "tr46": { 1758 | "version": "0.0.3", 1759 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1760 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", 1761 | "dev": true 1762 | }, 1763 | "tslib": { 1764 | "version": "1.11.1", 1765 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", 1766 | "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", 1767 | "dev": true 1768 | }, 1769 | "tslint": { 1770 | "version": "5.20.1", 1771 | "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", 1772 | "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", 1773 | "dev": true, 1774 | "requires": { 1775 | "@babel/code-frame": "^7.0.0", 1776 | "builtin-modules": "^1.1.1", 1777 | "chalk": "^2.3.0", 1778 | "commander": "^2.12.1", 1779 | "diff": "^4.0.1", 1780 | "glob": "^7.1.1", 1781 | "js-yaml": "^3.13.1", 1782 | "minimatch": "^3.0.4", 1783 | "mkdirp": "^0.5.1", 1784 | "resolve": "^1.3.2", 1785 | "semver": "^5.3.0", 1786 | "tslib": "^1.8.0", 1787 | "tsutils": "^2.29.0" 1788 | } 1789 | }, 1790 | "tsutils": { 1791 | "version": "2.29.0", 1792 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", 1793 | "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", 1794 | "dev": true, 1795 | "requires": { 1796 | "tslib": "^1.8.1" 1797 | } 1798 | }, 1799 | "type-check": { 1800 | "version": "0.3.2", 1801 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1802 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1803 | "dev": true, 1804 | "requires": { 1805 | "prelude-ls": "~1.1.2" 1806 | } 1807 | }, 1808 | "typescript": { 1809 | "version": "4.4.4", 1810 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", 1811 | "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", 1812 | "dev": true 1813 | }, 1814 | "universalify": { 1815 | "version": "0.1.2", 1816 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 1817 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 1818 | "dev": true 1819 | }, 1820 | "unpipe": { 1821 | "version": "1.0.0", 1822 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1823 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 1824 | "dev": true 1825 | }, 1826 | "uuid": { 1827 | "version": "3.3.2", 1828 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1829 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1830 | }, 1831 | "validator": { 1832 | "version": "13.0.0", 1833 | "resolved": "https://registry.npmjs.org/validator/-/validator-13.0.0.tgz", 1834 | "integrity": "sha512-anYx5fURbgF04lQV18nEQWZ/3wHGnxiKdG4aL8J+jEDsm98n/sU/bey+tYk6tnGJzm7ioh5FoqrAiQ6m03IgaA==", 1835 | "dev": true 1836 | }, 1837 | "vm2": { 1838 | "version": "3.9.6", 1839 | "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.6.tgz", 1840 | "integrity": "sha512-BF7euUjgO+ezsz2UKex9kO9M/PtDNOf+KEpiqNepZsgf1MT7JYfJEIvG8BoYhZMLAVjqevFJ0UmXNuETe8m5dQ==", 1841 | "dev": true, 1842 | "requires": { 1843 | "acorn": "^8.7.0", 1844 | "acorn-walk": "^8.2.0" 1845 | } 1846 | }, 1847 | "webidl-conversions": { 1848 | "version": "3.0.1", 1849 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1850 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", 1851 | "dev": true 1852 | }, 1853 | "whatwg-url": { 1854 | "version": "5.0.0", 1855 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1856 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", 1857 | "dev": true, 1858 | "requires": { 1859 | "tr46": "~0.0.3", 1860 | "webidl-conversions": "^3.0.0" 1861 | } 1862 | }, 1863 | "word-wrap": { 1864 | "version": "1.2.3", 1865 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 1866 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 1867 | "dev": true 1868 | }, 1869 | "wrappy": { 1870 | "version": "1.0.2", 1871 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1872 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1873 | "dev": true 1874 | }, 1875 | "xregexp": { 1876 | "version": "2.0.0", 1877 | "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", 1878 | "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", 1879 | "dev": true 1880 | } 1881 | } 1882 | } 1883 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@teammaestro/nestjs-common", 3 | "version": "20.1.1", 4 | "description": "Our common decorators, services, etc for NestJS projects", 5 | "keywords": [ 6 | "Typescript", 7 | "NestJS", 8 | "Seed" 9 | ], 10 | "main": "index.js", 11 | "typings": "index.d.ts", 12 | "author": "TeamMaestro", 13 | "license": "ISC", 14 | "homepage": "https://github.com/TeamMaestro/nestjs-common", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/TeamMaestro/nestjs-common.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/TeamMaestro/nestjs-common/issues" 21 | }, 22 | "scripts": { 23 | "test": "echo \"Error: no test specified\" && exit 1", 24 | "watch": "tsc -w", 25 | "start": "tsc", 26 | "prepack": "tsc", 27 | "postpublish": "git push && git push --tags", 28 | "postpack": "rm *.d.ts && rm *.js && rm -Rf modules" 29 | }, 30 | "peerDependencies": { 31 | "@nestjs/common": "^8.0", 32 | "@nestjs/core": "^8.0", 33 | "@nestjs/microservices": "^8.0", 34 | "@nestjs/passport": "^8.0", 35 | "@nestjs/testing": "^8.0", 36 | "@nestjs/swagger": "^4.6.0", 37 | "@teammaestro/node-common": "^4.0.0", 38 | "class-validator": "^0.12.2", 39 | "config": "^3.2", 40 | "js-yaml": "^3.13", 41 | "log4js": "^6.2", 42 | "passport": "~0.4.0", 43 | "reflect-metadata": "~0.1.13", 44 | "rxjs": "^6.5" 45 | }, 46 | "devDependencies": { 47 | "@nestjs/common": "^8.0", 48 | "@nestjs/core": "^8.0", 49 | "@nestjs/microservices": "^8.0", 50 | "@nestjs/passport": "^8.0", 51 | "@nestjs/testing": "^8.0", 52 | "@nestjs/swagger": "^4.6.0", 53 | "@teammaestro/node-common": "4.0.0", 54 | "@types/bluebird": "^3.5.27", 55 | "@types/config": "^0.0", 56 | "@types/node": "^12.12.38", 57 | "@types/passport": "~1.0.0", 58 | "@types/raven": "^2.5", 59 | "@types/redis": "^2.8", 60 | "class-validator": "^0.12.2", 61 | "config": "^3.2", 62 | "js-yaml": "^3.13", 63 | "log4js": "^6.2", 64 | "passport": "~0.4.0", 65 | "reflect-metadata": "~0.1.13", 66 | "rxjs": "^6.5", 67 | "tslint": "^5.18", 68 | "typescript": "^4.4" 69 | }, 70 | "dependencies": { 71 | "bluebird": "^3.5.5", 72 | "raven": "^2.6", 73 | "redis": "^2.8" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './modules'; 2 | -------------------------------------------------------------------------------- /src/modules/application-tokens.const.ts: -------------------------------------------------------------------------------- 1 | export const ApplicationTokens = { 2 | LoggerToken: 'LoggerToken', 3 | RedisStoreToken: 'RedisStoreToken', 4 | RedisClientToken: 'RedisClientToken' 5 | }; 6 | -------------------------------------------------------------------------------- /src/modules/classes/axios-exception-error.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from 'axios'; 2 | 3 | export class AxiosExceptionError extends Error { 4 | message: string; 5 | stack: string; 6 | original: any; 7 | 8 | constructor(error: AxiosError) { 9 | super(); 10 | 11 | if (error) { 12 | if (error.response) { 13 | this.message = `${error.response.status} - ${error.response.statusText}`; 14 | this.original = { 15 | config: error.config, 16 | response: { 17 | status: error.response.status, 18 | statusText: error.response.statusText, 19 | headers: error.response.headers, 20 | data: error.response.data 21 | } 22 | }; 23 | } 24 | else { 25 | this.message = error.message; 26 | } 27 | 28 | this.stack = error.stack; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/modules/classes/constructed-object.ts: -------------------------------------------------------------------------------- 1 | export class ConstructedObject { 2 | 3 | constructor(public readonly object: any) { 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/modules/classes/document-service-error.ts: -------------------------------------------------------------------------------- 1 | export class DocumentServiceExceptionError extends Error { 2 | message: string; 3 | stack: string; 4 | original: any; 5 | 6 | constructor(error: any) { 7 | super(); 8 | 9 | if (error) { 10 | this.message = error.message; 11 | this.stack = error.stack; 12 | this.original = error.original; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/modules/classes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './axios-exception-error'; 2 | export * from './document-service-error'; 3 | export * from './sql-exception-error'; 4 | export * from './constructed-object'; 5 | -------------------------------------------------------------------------------- /src/modules/classes/sql-exception-error.ts: -------------------------------------------------------------------------------- 1 | import { SequelizeError } from '../interfaces'; 2 | 3 | export class SqlExceptionError extends Error { 4 | message: string; 5 | stack: string; 6 | original: any; 7 | 8 | constructor(error: SequelizeError) { 9 | super(); 10 | 11 | if (error) { 12 | this.message = error.message; 13 | this.stack = error.stack; 14 | this.original = error.original; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/constants/do-not-validate.const.ts: -------------------------------------------------------------------------------- 1 | export const DO_NOT_VALIDATE = 'teamhive:do-no-validate'; 2 | -------------------------------------------------------------------------------- /src/modules/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './do-not-validate.const'; 2 | -------------------------------------------------------------------------------- /src/modules/decorators/api-docs/fetch-all-response.factory.ts: -------------------------------------------------------------------------------- 1 | import { HiveApiModelProperty } from './hive-api-property.decorator'; 2 | 3 | export const fetchAllResponseFactory: (contentClass: any) => any = (contentClass: any) => { 4 | class FetchAllResponseClass { 5 | @HiveApiModelProperty(`The count of total elements.`) 6 | totalElements: number; 7 | 8 | @HiveApiModelProperty('The content', { 9 | type: contentClass 10 | }) 11 | content: any[]; 12 | 13 | constructor(findAndCountAll: any) { 14 | this.totalElements = findAndCountAll.count; 15 | this.content = findAndCountAll.rows; 16 | } 17 | } 18 | return FetchAllResponseClass; 19 | }; 20 | -------------------------------------------------------------------------------- /src/modules/decorators/api-docs/hive-api-docs.decorator.ts: -------------------------------------------------------------------------------- 1 | import { HttpCode } from '@nestjs/common'; 2 | import { METHOD_METADATA } from '@nestjs/common/constants'; 3 | import { RequestMethod } from '@nestjs/common/enums/request-method.enum'; 4 | import { 5 | ApiBadRequestResponse, 6 | ApiBearerAuth, 7 | ApiHeaders, 8 | ApiInternalServerErrorResponse, 9 | ApiNotFoundResponse, 10 | ApiOperation, 11 | ApiResponse 12 | } from '@nestjs/swagger'; 13 | import { HiveApiDocConfig } from '../../interfaces'; 14 | 15 | export function HiveApiDocs(options: HiveApiDocConfig): MethodDecorator { 16 | return (target, propertyKey, descriptor) => { 17 | const method = RequestMethod[Reflect.getMetadata(METHOD_METADATA, descriptor.value)]; 18 | const requiresAuth = !Reflect.getMetadata('isUnauthenticated', descriptor.value); 19 | 20 | let apiDescription = `class ${target.constructor.name}.${propertyKey.toString()}`; 21 | if (options.description) { 22 | apiDescription += ` -- ${options.description}`; 23 | } 24 | 25 | // Add ApiOperation Decorator 26 | ApiOperation({ 27 | summary: options.summary, 28 | description: apiDescription, 29 | deprecated: options.deprecated || false 30 | })(target, propertyKey, descriptor); 31 | 32 | // Add ApiResponse Decorator 33 | ApiResponse({ 34 | ...options.response 35 | })(target, propertyKey, descriptor); 36 | 37 | // Add HttpCode Decorator 38 | HttpCode(options.response.status)(target, propertyKey, descriptor); 39 | 40 | // Add Not Found Response 41 | ApiNotFoundResponse({ 42 | description: 'Entity Not Found' 43 | })(target, propertyKey, descriptor); 44 | 45 | // Add Internal Server Error 46 | ApiInternalServerErrorResponse({ 47 | description: 'Internal Server Error' 48 | })(target, propertyKey, descriptor); 49 | 50 | // Add Bad Request Response - dynamically add based on POST | PUT Methods 51 | if (['POST', 'PUT'].includes(method)) { 52 | ApiBadRequestResponse({ 53 | description: 'Bad Request' 54 | })(target, propertyKey, descriptor); 55 | } 56 | 57 | // Add Not Found Response - dynamically added based IsUnauthenticated Decorator 58 | if (requiresAuth) { 59 | ApiBearerAuth()(target, propertyKey, descriptor); 60 | } 61 | 62 | // Add the ApiHeaders if supplied 63 | if (options.headers?.length > 0) { 64 | ApiHeaders(options.headers)(target, propertyKey, descriptor); 65 | } 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /src/modules/decorators/api-docs/hive-api-property.decorator.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty, ApiPropertyOptions } from '@nestjs/swagger'; 2 | import { ValidationTypes } from 'class-validator'; 3 | import { getValidation } from '../../functions/get-validation.function'; 4 | 5 | export function HiveApiModelProperty(description: string, metadata: ApiPropertyOptions = {}): PropertyDecorator { 6 | return (target, propertyKey: string) => { 7 | const validations = getValidation(target.constructor, propertyKey); 8 | 9 | metadata.required = 10 | metadata.required !== undefined 11 | ? metadata.required 12 | : !validations.includes(ValidationTypes.CONDITIONAL_VALIDATION); 13 | 14 | ApiProperty({ 15 | description, 16 | ...metadata 17 | })(target, propertyKey); 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/modules/decorators/api-docs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fetch-all-response.factory'; 2 | export * from './hive-api-docs.decorator'; 3 | export * from './hive-api-property.decorator'; 4 | -------------------------------------------------------------------------------- /src/modules/decorators/critical.decorator.ts: -------------------------------------------------------------------------------- 1 | import { SetMetadata } from '@nestjs/common'; 2 | 3 | export const Critical = (critical: boolean = true) => SetMetadata('markedCritical', critical); 4 | -------------------------------------------------------------------------------- /src/modules/decorators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api-docs'; 2 | export * from './inject-metadata'; 3 | export * from './critical.decorator'; 4 | export * from './permissions.decorator'; 5 | export * from './query-user.decorator'; 6 | export * from './user.decorator'; 7 | export * from './rpc'; 8 | -------------------------------------------------------------------------------- /src/modules/decorators/inject-metadata/index.ts: -------------------------------------------------------------------------------- 1 | export * from './inject-metadata.decorator'; 2 | export * from './injected-dto.type'; 3 | export * from './injected-metadata.interface'; 4 | -------------------------------------------------------------------------------- /src/modules/decorators/inject-metadata/inject-metadata.decorator.ts: -------------------------------------------------------------------------------- 1 | import { Body, createParamDecorator, ExecutionContext, Query } from '@nestjs/common'; 2 | import { DO_NOT_VALIDATE } from '../../constants'; 3 | 4 | export const INJECTED_METADATA_KEY = Symbol('INJECTED METADATA KEY'); 5 | /** 6 | * This parameter decorator will pull the given property off of the request and inject the the results of the 7 | * injectFunctions on to the key [INJECTED_METADATA_KEY]. This constructed object is then the first parameter for when 8 | * the validation pipe constructs the decorated parameter 9 | * @param reqProperty property off of the request object (body, query, etc.) 10 | * @param injectFunctions functions that run at runtime, return data, and are merged into an object that is on the [INJECTED_METADATA_KEY] 11 | */ 12 | export const InjectMetadata = (reqProperty?: string, ...injectFunctions: ( 13 | (req, paramTarget, paramProperty, paramIndex) => object)[] 14 | ) => { 15 | // return the a custom decorator 16 | return (target, property, index) => { 17 | if (reqProperty === 'body') { 18 | Body(DO_NOT_VALIDATE)(target, property, index); 19 | } 20 | else if (reqProperty === 'query') { 21 | Query(DO_NOT_VALIDATE)(target, property, index); 22 | } 23 | // create a nest parameter decorator that will have access to the request object 24 | createParamDecorator((data, ctx: ExecutionContext): any => { 25 | const req = ctx.switchToHttp().getRequest(); 26 | // if property is not a object, return that value 27 | if (!reqProperty || typeof req[reqProperty] !== 'object') { 28 | return req[reqProperty]; 29 | } 30 | 31 | // inject request and parameter data into the inject functions and merge their results 32 | const reqValue = req[reqProperty]; 33 | const [paramTarget, paramProperty, paramIndex] = data; 34 | let injectedMetadata = {}; 35 | injectFunctions.forEach((fn) => { 36 | injectedMetadata = Object.assign(injectedMetadata, fn(ctx, paramTarget, paramProperty, paramIndex)); 37 | }); 38 | return { 39 | ...reqValue, 40 | [INJECTED_METADATA_KEY]: injectedMetadata 41 | }; 42 | }) 43 | // pass the decorator information into the data parameter of the nest parameter decorator 44 | ([target, property, index]) 45 | // call the parameter decorator based on the information received from the custom decorator 46 | (target, property, index); 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /src/modules/decorators/inject-metadata/injected-dto.type.ts: -------------------------------------------------------------------------------- 1 | import { InjectedMetadata } from './injected-metadata.interface'; 2 | 3 | export type InjectedDto = DtoType & InjectedMetadata; 4 | -------------------------------------------------------------------------------- /src/modules/decorators/inject-metadata/injected-metadata.interface.ts: -------------------------------------------------------------------------------- 1 | import { INJECTED_METADATA_KEY } from './inject-metadata.decorator'; 2 | 3 | export interface InjectedMetadata { 4 | [INJECTED_METADATA_KEY]: Pick; 5 | } 6 | -------------------------------------------------------------------------------- /src/modules/decorators/permissions.decorator.ts: -------------------------------------------------------------------------------- 1 | import { SetMetadata } from '@nestjs/common'; 2 | 3 | export const Permissions = (...permissions: T[]) => SetMetadata('permissions', permissions); 4 | -------------------------------------------------------------------------------- /src/modules/decorators/query-user.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator, ExecutionContext } from '@nestjs/common'; 2 | 3 | export const QueryUser = createParamDecorator((_data, ctx: ExecutionContext): any => { 4 | const req = ctx.switchToHttp().getRequest(); 5 | return { 6 | user: req.user || {}, 7 | ...req.query 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /src/modules/decorators/rpc/get-message-options.ts: -------------------------------------------------------------------------------- 1 | import { HiveMessageOptions, RPC_OPTIONS_KEY } from './hive-message.decorator'; 2 | 3 | export function getMessageOptions(target: any): HiveMessageOptions { 4 | return Reflect.getMetadata(RPC_OPTIONS_KEY, target) ?? {}; 5 | } 6 | -------------------------------------------------------------------------------- /src/modules/decorators/rpc/get-message-pattern.ts: -------------------------------------------------------------------------------- 1 | import { RPC_PATTERN_KEY } from './hive-message.decorator'; 2 | 3 | export function getMessagePattern(target: any) { 4 | return Reflect.getMetadata(RPC_PATTERN_KEY, target); 5 | } 6 | -------------------------------------------------------------------------------- /src/modules/decorators/rpc/hive-message-handler.decorator.ts: -------------------------------------------------------------------------------- 1 | import { EventPattern, MessagePattern } from '@nestjs/microservices'; 2 | import { getMessageOptions } from './get-message-options'; 3 | import { getMessagePattern } from './get-message-pattern'; 4 | 5 | export function HiveMessageHandler(messageClass: any): MethodDecorator { 6 | return (target, property, descriptor) => { 7 | const messagePattern = getMessagePattern(messageClass); 8 | const messageOptions = getMessageOptions(messageClass); 9 | if (messageOptions.isRpc) { 10 | MessagePattern(messagePattern)(target, property, descriptor); 11 | } else { 12 | EventPattern(messagePattern)(target, property, descriptor); 13 | } 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/modules/decorators/rpc/hive-message.decorator.ts: -------------------------------------------------------------------------------- 1 | import { Critical } from '../critical.decorator'; 2 | 3 | export const RPC_PATTERN_KEY = 'loop:rpc-pattern-key'; 4 | export const RPC_OPTIONS_KEY = 'teamhive:rpc-message-options'; 5 | 6 | export interface HiveMessageOptions { 7 | isRpc?: boolean; 8 | } 9 | 10 | export function HiveMessage(messagePattern: string, options: HiveMessageOptions = {}): ClassDecorator { 11 | return (target) => { 12 | Critical()(target); 13 | Reflect.defineMetadata(RPC_PATTERN_KEY, messagePattern, target); 14 | Reflect.defineMetadata(RPC_OPTIONS_KEY, options, target); 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/modules/decorators/rpc/index.ts: -------------------------------------------------------------------------------- 1 | export * from './get-message-pattern'; 2 | export * from './hive-message-handler.decorator'; 3 | export * from './hive-message.decorator'; 4 | -------------------------------------------------------------------------------- /src/modules/decorators/user.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator, ExecutionContext } from '@nestjs/common'; 2 | 3 | export const User = createParamDecorator((_data, ctx: ExecutionContext) => { 4 | const req = ctx.switchToHttp().getRequest(); 5 | return req.user || {}; 6 | }); 7 | -------------------------------------------------------------------------------- /src/modules/dtos/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pagination-search.dto'; 2 | export * from './pagination.dto'; 3 | -------------------------------------------------------------------------------- /src/modules/dtos/pagination-search.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsOptional } from 'class-validator'; 2 | import { Pagination } from './pagination.dto'; 3 | import { HiveApiModelProperty } from '../decorators'; 4 | import { PaginationOptions } from '../interfaces'; 5 | 6 | export class PaginationSearch extends Pagination { 7 | @HiveApiModelProperty(`Term to search by`) 8 | @IsOptional() 9 | search: string | any; 10 | 11 | searchTerm: string; 12 | 13 | constructor(options: PaginationOptions, defaultSortBy: string) { 14 | super(options, defaultSortBy); 15 | 16 | this.searchTerm = options.search; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/modules/dtos/pagination.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsIn, IsOptional, IsString } from 'class-validator'; 2 | import { HiveApiModelProperty } from '../decorators'; 3 | import { PaginationOptions } from '../interfaces'; 4 | import { SortDirection } from '../types'; 5 | import { IsNumber } from '../validators'; 6 | 7 | export class Pagination { 8 | @HiveApiModelProperty(`The number of page to fetch`) 9 | @IsNumber() 10 | @IsOptional() 11 | page: number; 12 | 13 | @HiveApiModelProperty(`The number of results to fetch`) 14 | @IsNumber() 15 | @IsOptional() 16 | size: number; 17 | 18 | @HiveApiModelProperty(`The number of results to shift the offset by`) 19 | @IsNumber() 20 | @IsOptional() 21 | shift: number; 22 | 23 | @HiveApiModelProperty(`Property to sort by`) 24 | @IsString() 25 | @IsOptional() 26 | sortBy: string; 27 | 28 | @HiveApiModelProperty(`sort direction: ASC, DESC`) 29 | @IsString() 30 | @IsOptional() 31 | @IsIn(['ASC', 'DESC']) 32 | sortDir: SortDirection; 33 | 34 | offset: number; 35 | sortByModel: any; 36 | defaultSort: string; 37 | 38 | secondarySort?: any; 39 | 40 | static defaultSortDir: SortDirection = 'DESC'; 41 | 42 | constructor(options: PaginationOptions, defaultSortBy: string) { 43 | this.defaultSort = defaultSortBy; 44 | 45 | options.page = options.page === undefined ? 0 : +options.page; 46 | options.size = options.size === undefined ? 10 : +options.size; 47 | options.shift = options.shift === undefined ? 0 : +options.shift; 48 | 49 | // If size or page is -1, then leave these undefined 50 | if (Number(options.size) >= 0 && Number(options.page) >= 0) { 51 | this.page = options.page; 52 | this.size = options.size; 53 | this.shift = options.shift; 54 | this.offset = this.page * this.size + this.shift; 55 | 56 | // if offset less than 0 due to shift, set to zero 57 | if (this.offset < 0) { 58 | this.offset = 0; 59 | } 60 | } 61 | this.sortBy = options.sortBy || defaultSortBy; 62 | this.sortDir = options.sortDir || Pagination.defaultSortDir; 63 | } 64 | 65 | getOrderBy(options = {} as OrderByOptions) { 66 | const sortBy = []; 67 | 68 | // primary sort 69 | if (this.sortByModel) { 70 | if (Array.isArray(this.sortByModel)) { 71 | sortBy.push([...this.sortByModel, this.sortBy, this.sortDir]); 72 | } else { 73 | sortBy.push([this.sortByModel, this.sortBy, this.sortDir]); 74 | } 75 | } else { 76 | sortBy.push([this.sortBy, this.sortDir]); 77 | } 78 | 79 | //secondary sort 80 | if (this.secondarySort) { 81 | sortBy.push(this.secondarySort); 82 | } 83 | 84 | // default sort by to ensure consistent pagination regardless of collisions 85 | if (options.sortById !== false) { 86 | const sortByKey = options.sortBy || 'id'; 87 | sortBy.push([sortByKey, 'asc']); 88 | } 89 | 90 | return sortBy; 91 | } 92 | } 93 | 94 | export interface OrderByOptions { 95 | sortById?: boolean; 96 | sortBy?: string; 97 | } 98 | -------------------------------------------------------------------------------- /src/modules/enums/index.ts: -------------------------------------------------------------------------------- 1 | export * from './passport-whitelisted-errors.enum'; 2 | -------------------------------------------------------------------------------- /src/modules/enums/passport-whitelisted-errors.enum.ts: -------------------------------------------------------------------------------- 1 | export enum PassportWhitelistedErrors { 2 | NoAuthToken = 'No auth token', 3 | JwtExpired = 'jwt expired', 4 | JwtNotActive = 'jwt not active', 5 | InvalidSignature = 'invalid signature', 6 | MaxAgeExceeded = 'maxAge exceeded', 7 | JwtMalformed = 'jwt malformed', 8 | UnexpectedToken = 'Unexpected token', 9 | InvalidToken = 'invalid token' 10 | } 11 | -------------------------------------------------------------------------------- /src/modules/exceptions/axios.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { AxiosError } from 'axios'; 3 | import { LoggedException } from './logged.exception'; 4 | import { AxiosExceptionError } from '../classes'; 5 | 6 | export class AxiosException extends LoggedException { 7 | constructor(error: AxiosError, customMessage?: string) { 8 | super( 9 | 'AxiosException', 10 | customMessage || 'Internal server error with request', 11 | HttpStatus.INTERNAL_SERVER_ERROR, 12 | new AxiosExceptionError(error) 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/modules/exceptions/bad-request.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { PassiveException } from './passive.exception'; 3 | 4 | export class BadRequestException extends PassiveException { 5 | constructor( 6 | message?: any, 7 | options?: { 8 | appCode?: string; 9 | } 10 | ) { 11 | super('BadRequestException', message || 'Bad Request', HttpStatus.BAD_REQUEST); 12 | 13 | if (options?.appCode) { 14 | this.appCode = options.appCode; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/exceptions/base.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpException } from "@nestjs/common"; 2 | 3 | export class BaseException extends HttpException { 4 | customResponse: { 5 | [key: string]: any; 6 | }; 7 | 8 | tags: { 9 | [key: string]: 10 | | number 11 | | string 12 | | boolean 13 | | bigint 14 | | symbol 15 | | null 16 | | undefined; 17 | }; 18 | 19 | error: Error; 20 | 21 | loggedMetadata: any; 22 | 23 | // This will be exposed in responses by the Filters 24 | appCode?: string; 25 | 26 | constructor( 27 | name: string, 28 | response: string | object, 29 | status: number, 30 | error?: Error 31 | ) { 32 | super(response, status); 33 | this.name = name; 34 | this.error = error; 35 | this.tags = {}; 36 | this.loggedMetadata = {}; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/modules/exceptions/conflict.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { PassiveException } from './passive.exception'; 3 | 4 | export class ConflictException extends PassiveException { 5 | constructor(error: any, conflictDescription: string) { 6 | super('ConflictException', conflictDescription, HttpStatus.CONFLICT, error); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/modules/exceptions/document-service.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { LoggedException } from './logged.exception'; 3 | import { DocumentServiceExceptionError } from '../classes/document-service-error'; 4 | 5 | export class DocumentServiceException extends LoggedException { 6 | constructor(error) { 7 | super( 8 | 'DocumentServiceException', 9 | 'Internal Server Error - DMS', 10 | HttpStatus.INTERNAL_SERVER_ERROR, 11 | new DocumentServiceExceptionError(error) 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/modules/exceptions/forbidden.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { PassiveException } from './passive.exception'; 3 | 4 | export class ForbiddenException extends PassiveException { 5 | constructor( 6 | message: string = 'Forbidden Action', 7 | options?: { 8 | appCode?: string; 9 | } 10 | ) { 11 | super('ForbiddenException', message, HttpStatus.FORBIDDEN); 12 | 13 | if (options?.appCode) { 14 | this.appCode = options.appCode; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/exceptions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './axios.exception'; 2 | export * from './bad-request.exception'; 3 | export * from './base.exception'; 4 | export * from './conflict.exception'; 5 | export * from './document-service.exception'; 6 | export * from './forbidden.exception'; 7 | export * from './logged.exception'; 8 | export * from './not-found.exception'; 9 | export * from './passive.exception'; 10 | export * from './processing.exception'; 11 | export * from './redirect.exception'; 12 | export * from './redis.exception'; 13 | export * from './sql.exception'; 14 | export * from './sso.exception'; 15 | export * from './unauthorized.exception'; 16 | export * from './validation.exception'; 17 | -------------------------------------------------------------------------------- /src/modules/exceptions/logged.exception.ts: -------------------------------------------------------------------------------- 1 | import { BaseException } from './base.exception'; 2 | 3 | export class LoggedException extends BaseException { 4 | /** 5 | * These tags will be added to the error when captured in sentry. 6 | */ 7 | tags: { 8 | [key: string]: string; 9 | } = { 10 | critical: 'true' 11 | }; 12 | 13 | constructor(name: string, response: string | object, status: number, error?: Error) { 14 | super(name, response, status, error); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/modules/exceptions/not-found.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { PassiveException } from './passive.exception'; 3 | 4 | export class NotFoundException extends PassiveException { 5 | constructor( 6 | message?: string, 7 | options?: { 8 | appCode?: string; 9 | } 10 | ) { 11 | super('NotFoundException', message || 'Item not found', HttpStatus.NOT_FOUND); 12 | 13 | if (options?.appCode) { 14 | this.appCode = options.appCode; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/exceptions/passive.exception.ts: -------------------------------------------------------------------------------- 1 | import { BaseException } from './base.exception'; 2 | 3 | export class PassiveException extends BaseException { 4 | constructor(name: string, response: string | object, status: number, error?: Error) { 5 | super(name, response, status, error); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/modules/exceptions/processing.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { PassiveException } from './passive.exception'; 3 | 4 | export class ProcessingException extends PassiveException { 5 | constructor(message?: string) { 6 | super('ProcessingException', message || 'Processing', HttpStatus.ACCEPTED); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/modules/exceptions/redirect.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { BaseException } from './base.exception'; 3 | 4 | export class RedirectException extends BaseException { 5 | redirectPath: string; 6 | 7 | constructor(redirectPath: string) { 8 | super('RedirectException', 'Redirect', HttpStatus.TEMPORARY_REDIRECT); 9 | this.redirectPath = redirectPath; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/exceptions/redis.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { LoggedException } from './logged.exception'; 3 | 4 | export class RedisException extends LoggedException { 5 | constructor(error?: any) { 6 | super('RedisException', 'Internal server error with redis', HttpStatus.INTERNAL_SERVER_ERROR, error); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/modules/exceptions/sql.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { LoggedException } from './logged.exception'; 3 | import { SqlExceptionError } from '../classes'; 4 | import { SequelizeError } from '../interfaces'; 5 | 6 | export class SqlException extends LoggedException { 7 | constructor(error: SequelizeError, customMessage?: string) { 8 | super( 9 | 'SqlException', 10 | customMessage || 'Internal server error with database', 11 | HttpStatus.INTERNAL_SERVER_ERROR, 12 | new SqlExceptionError(error) 13 | ); 14 | this.loggedMetadata = { 15 | sql: error.sql 16 | }; 17 | if (error.original) { 18 | this.loggedMetadata.original = { 19 | message: error.original.message, 20 | detail: error.original.detail 21 | }; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/modules/exceptions/sso.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { LoggedException } from './logged.exception'; 3 | 4 | export class SsoException extends LoggedException { 5 | constructor(error?: Error, customMessage?: string) { 6 | super( 7 | 'SsoException', 8 | customMessage || 'Internal server error with SSO', 9 | HttpStatus.INTERNAL_SERVER_ERROR, 10 | error 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/modules/exceptions/unauthorized.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { PassiveException } from './passive.exception'; 3 | 4 | export class UnauthorizedException extends PassiveException { 5 | constructor( 6 | message?: any, 7 | options?: { 8 | appCode?: string; 9 | } 10 | ) { 11 | super('UnauthorizedException', message || 'Unauthorized', HttpStatus.UNAUTHORIZED); 12 | 13 | if (options?.appCode) { 14 | this.appCode = options.appCode; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/exceptions/validation.exception.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { LoggedException } from './logged.exception'; 3 | 4 | export class ValidationException extends LoggedException { 5 | constructor( 6 | message?: string, 7 | options?: { 8 | appCode?: string; 9 | critical?: boolean; 10 | } 11 | ) { 12 | super('ValidationException', message || 'Request format is invalid', HttpStatus.BAD_REQUEST); 13 | 14 | this.tags.critical = !!options?.critical ? 'true' : 'false'; 15 | 16 | if (options?.appCode) { 17 | this.appCode = options.appCode; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/modules/filters/base-http-exception.filter.ts: -------------------------------------------------------------------------------- 1 | import { ArgumentsHost, ContextType, HttpStatus, Injectable } from '@nestjs/common'; 2 | import * as express from 'express'; 3 | import { EMPTY, throwError } from 'rxjs'; 4 | import { ActivemqMicroserviceContext } from '../interfaces/activemq-microservice-context.interface'; 5 | import { ErrorHandler } from '../services/error-handler/error-handler.service'; 6 | 7 | @Injectable() 8 | export class BaseHttpExceptionFilter { 9 | constructor( 10 | protected readonly errorHandler: ErrorHandler 11 | ) { 12 | } 13 | 14 | getInitialException(exception: any) { 15 | // use 405 instead of 403 do to cloud front's handling of forbidden 16 | if (exception.status === HttpStatus.FORBIDDEN) { 17 | exception.status = HttpStatus.METHOD_NOT_ALLOWED; 18 | } 19 | return exception; 20 | } 21 | 22 | /** 23 | * check for fields on response specific to http to determine context type 24 | * @param host 25 | */ 26 | getHostContextType(host: ArgumentsHost): ContextType { 27 | const res: express.Response = host.switchToHttp().getResponse(); 28 | if (res.req && res.status && typeof res.status === 'function') { 29 | return 'http'; 30 | } 31 | return 'rpc'; 32 | } 33 | 34 | handleRpcException(exception: any, host: ArgumentsHost, logException: boolean) { 35 | const context = host.switchToRpc().getContext(); 36 | 37 | if (context.rpcType === 'ACTIVE_MQ') { 38 | if (logException) { 39 | this.errorHandler.captureException(exception); 40 | } 41 | 42 | return throwError(exception); 43 | } else { 44 | return EMPTY; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/modules/filters/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base-http-exception.filter'; 2 | export * from './logged-http-exception.filter'; 3 | export * from './passive-http-exception.filter'; 4 | export * from './redirect-http-exception.filter'; 5 | export * from './uncaught-exception.filter'; 6 | -------------------------------------------------------------------------------- /src/modules/filters/logged-http-exception.filter.ts: -------------------------------------------------------------------------------- 1 | import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common'; 2 | import { Response } from 'express'; 3 | import { BaseHttpExceptionFilter } from './base-http-exception.filter'; 4 | import { LoggedException } from '../exceptions/logged.exception'; 5 | 6 | @Catch(LoggedException) 7 | export class LoggedHttpExceptionFilter extends BaseHttpExceptionFilter implements ExceptionFilter { 8 | catch(exception: LoggedException, host: ArgumentsHost) { 9 | // get the original exception if it was caught more than once 10 | exception = this.getInitialException(exception) as LoggedException; 11 | 12 | // determine the context type 13 | const contextType = this.getHostContextType(host); 14 | 15 | // if http, then form response 16 | if (contextType === 'http') { 17 | this.errorHandler.captureException(exception); 18 | 19 | const res: Response = host.switchToHttp().getResponse(); 20 | 21 | const statusCode = exception.getStatus() || 500; 22 | const exceptionResponse = { 23 | statusCode, 24 | appCode: exception.appCode ?? HttpStatus[statusCode], 25 | message: exception.getResponse(), 26 | ...exception.customResponse 27 | }; 28 | 29 | res.status(statusCode).json(exceptionResponse); 30 | } else { 31 | return this.handleRpcException(exception, host, true); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/modules/filters/passive-http-exception.filter.ts: -------------------------------------------------------------------------------- 1 | import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common'; 2 | import { Response } from 'express'; 3 | import { BaseHttpExceptionFilter } from './base-http-exception.filter'; 4 | import { PassiveException } from '../exceptions/passive.exception'; 5 | 6 | @Catch(PassiveException) 7 | export class PassiveHttpExceptionFilter extends BaseHttpExceptionFilter implements ExceptionFilter { 8 | catch(exception: PassiveException, host: ArgumentsHost) { 9 | // determine the context type 10 | const contextType = this.getHostContextType(host); 11 | 12 | // if http, then form response 13 | if (contextType === 'http') { 14 | const res: Response = host.switchToHttp().getResponse(); 15 | 16 | // get the original exception if it was caught more than once 17 | exception = this.getInitialException(exception); 18 | 19 | const statusCode = exception.getStatus() || 500; 20 | const exceptionResponse = { 21 | statusCode, 22 | appCode: exception.appCode ?? HttpStatus[statusCode], 23 | message: exception.getResponse(), 24 | ...exception.customResponse 25 | }; 26 | 27 | res.status(statusCode).json(exceptionResponse); 28 | } else { 29 | return this.handleRpcException(exception, host, false); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/modules/filters/redirect-http-exception.filter.ts: -------------------------------------------------------------------------------- 1 | import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common'; 2 | import { Response, Request } from 'express'; 3 | import { BaseHttpExceptionFilter } from './base-http-exception.filter'; 4 | import { RedirectException } from '../exceptions'; 5 | 6 | @Catch(RedirectException) 7 | export class RedirectHttpExceptionFilter extends BaseHttpExceptionFilter implements ExceptionFilter { 8 | catch(exception: RedirectException, host: ArgumentsHost) { 9 | const res: Response = host.switchToHttp().getResponse(); 10 | const req: Request = host.switchToHttp().getRequest(); 11 | 12 | // get the original exception if it was caught more than once 13 | exception = this.getInitialException(exception); 14 | 15 | // prepend scheme to redirect if exists on request 16 | let scheme = ''; 17 | if (req['customUrlScheme']) { 18 | scheme = req['customUrlScheme']; 19 | } 20 | 21 | res.redirect(`${scheme}${exception.redirectPath}`); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/modules/filters/uncaught-exception.filter.ts: -------------------------------------------------------------------------------- 1 | import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common'; 2 | import { Response } from 'express'; 3 | import { BaseHttpExceptionFilter } from './base-http-exception.filter'; 4 | 5 | const ignoredHttpStatuses = [ 6 | HttpStatus.NOT_FOUND, 7 | HttpStatus.FORBIDDEN, 8 | HttpStatus.METHOD_NOT_ALLOWED, 9 | HttpStatus.PAYLOAD_TOO_LARGE 10 | ]; 11 | 12 | @Catch() 13 | export class UncaughtExceptionFilter extends BaseHttpExceptionFilter implements ExceptionFilter { 14 | catch(exception: any, host: ArgumentsHost) { 15 | // get the original exception if it was caught more than once 16 | exception = this.getInitialException(exception); 17 | 18 | // determine the context type 19 | const contextType = this.getHostContextType(host); 20 | 21 | // if http, then form response 22 | if (contextType === 'http') { 23 | // handle stack traces 24 | if (ignoredHttpStatuses.indexOf(exception.status) === -1) { 25 | this.errorHandler.captureException(exception); 26 | } 27 | 28 | const res: Response = host.switchToHttp().getResponse(); 29 | 30 | const statusCode = exception.status || 500; 31 | 32 | let message; 33 | if (exception.message) { 34 | message = exception.message.error || exception.message; 35 | } else { 36 | message = 'There was an internal server error'; 37 | } 38 | 39 | const exceptionResponse = { 40 | statusCode, 41 | appCode: exception.appCode ?? HttpStatus[statusCode], 42 | message, 43 | ...exception.customResponse 44 | }; 45 | 46 | res.status(statusCode).json(exceptionResponse); 47 | } else { 48 | return this.handleRpcException(exception, host, true); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/modules/functions/cookie-to-bearer.function.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | 3 | export function CookieToBearer(cookieName: string = 'access_token') { 4 | return (req: express.Request, res: express.Response, next: express.NextFunction) => { 5 | 6 | if (req.cookies && req.cookies[cookieName]) { 7 | req.headers['authorization'] = `bearer ${req.cookies[cookieName]}`; 8 | } 9 | next(); 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/functions/get-validation.function.ts: -------------------------------------------------------------------------------- 1 | import { getFromContainer, MetadataStorage, ValidationTypes } from 'class-validator'; 2 | 3 | // tslint:disable-next-line:ban-types 4 | export function getValidation(targetConstructor: Function, propertyKey: string): ValidationTypes[] { 5 | const validatorStorage: MetadataStorage = getFromContainer(MetadataStorage); 6 | 7 | const targetValidationMetadata = validatorStorage.getTargetValidationMetadatas(targetConstructor, null, null); 8 | 9 | const validationMetadata = validatorStorage.groupByPropertyName(targetValidationMetadata); 10 | if (validationMetadata) { 11 | const propertyValidation = validationMetadata[propertyKey]; 12 | if (propertyValidation && propertyValidation.length > 0) { 13 | return propertyValidation.map(validation => validation.type); 14 | } 15 | } 16 | return []; 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/functions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cookie-to-bearer.function'; 2 | export * from './get-validation.function'; 3 | export * from './setup-swagger-docs.function'; 4 | export * from './write-json.function'; 5 | -------------------------------------------------------------------------------- /src/modules/functions/setup-swagger-docs.function.ts: -------------------------------------------------------------------------------- 1 | import { INestApplication } from '@nestjs/common'; 2 | import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger'; 3 | import { join } from 'path'; 4 | import { writeJson } from './write-json.function'; 5 | 6 | export async function setupSwaggerDocs( 7 | app: INestApplication, 8 | outputDirectory = process.cwd(), 9 | applicationName = 'API' 10 | ) { 11 | // Setup Swagger Documents 12 | const options = new DocumentBuilder() 13 | .setTitle(applicationName) 14 | .setDescription(`Documentation for ${applicationName}`) 15 | .addBearerAuth() 16 | .build(); 17 | const document = SwaggerModule.createDocument(app, options); 18 | 19 | // Fix operationIds for proper navigation 20 | processDocument(document); 21 | 22 | // Write the Json to directory 23 | await writeJson(join(outputDirectory, 'openApi.json'), JSON.stringify(document)); 24 | } 25 | 26 | function processDocument(document: OpenAPIObject): OpenAPIObject { 27 | // Available methods we need to update operationIds 28 | const methods = ['get', 'put', 'post', 'delete']; 29 | const tags = new Set(); 30 | // Go through document paths and methods to prepend the tag (name of controller) 31 | if (document && document.paths) { 32 | const paths = Object.keys(document.paths); 33 | for (const path of paths) { 34 | for (const method of methods) { 35 | if (document.paths[path][method] && document.paths[path][method].operationId) { 36 | const operationId = document.paths[path][method].operationId; 37 | if (document.paths[path][method].tags && document.paths[path][method].tags.length > 0) { 38 | let tag = document.paths[path][method].tags[0]; 39 | tags.add(tag); 40 | if (tag && tag.length > 0) { 41 | tag = tag.replace(/\s/g, ''); 42 | } 43 | document.paths[path][method].operationId = `${encodeURI(tag)}/${operationId}`; 44 | } 45 | } 46 | } 47 | } 48 | } 49 | const sortedTags = Array.from(tags) 50 | .sort((strA, strB) => { 51 | const strANoAdmin = strA.replace('Admin - ', ''); 52 | const strBNoAdmin = strB.replace('Admin - ', ''); 53 | return strANoAdmin.localeCompare(strBNoAdmin, 'en'); 54 | }) 55 | .map((tagName) => ({ 56 | name: tagName 57 | })); 58 | document.tags = sortedTags; 59 | 60 | return document; 61 | } 62 | -------------------------------------------------------------------------------- /src/modules/functions/write-json.function.ts: -------------------------------------------------------------------------------- 1 | import { promises } from 'fs'; 2 | 3 | export async function writeJson(path: string, object: any) { 4 | const { writeFile } = promises; 5 | await writeFile(path, object); 6 | } 7 | -------------------------------------------------------------------------------- /src/modules/guards/index.ts: -------------------------------------------------------------------------------- 1 | export * from './permissions.guard'; 2 | export * from './passport-auth.guard'; 3 | -------------------------------------------------------------------------------- /src/modules/guards/passport-auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { AuthGuard } from '@nestjs/passport'; 2 | import { UnauthorizedException } from '../exceptions'; 3 | import { PassportWhitelistedErrors } from '../enums'; 4 | 5 | export function PassportAuthGuard(strategyToken: string) { 6 | return class Guard extends AuthGuard(strategyToken) { 7 | whitelistedErrors: Set; 8 | 9 | constructor() { 10 | super(); 11 | 12 | this.whitelistedErrors = new Set(Object.values(PassportWhitelistedErrors)); 13 | } 14 | 15 | handleRequest(error, user, passportError) { 16 | if (error || !user) { 17 | // if the error is not an instance of our UnauthorizedException, then capture 18 | if (!(error instanceof UnauthorizedException)) { 19 | let actualError = error; 20 | 21 | if (!actualError && passportError instanceof Error) { 22 | actualError = passportError; 23 | } 24 | if (!actualError) { 25 | actualError = new Error('Passport Error'); 26 | } 27 | 28 | // only handle (report to sentry) if not a whitelisted error 29 | if (!this.isWhitelisted(actualError)) { 30 | this.handleErrors(actualError); 31 | } 32 | } 33 | 34 | this.throwException(); 35 | } 36 | 37 | return user; 38 | } 39 | 40 | isWhitelisted(error = {} as Error) { 41 | return Array.from(this.whitelistedErrors).some((whitelistedError) => { 42 | if (typeof error.message === 'string') { 43 | return error.message.includes(whitelistedError); 44 | } 45 | return false; 46 | }); 47 | } 48 | 49 | handleErrors(error: Error) { 50 | console.warn('Override the handleErrors method in the child class of PassportAuthGuard to report errors!'); 51 | console.error(error); 52 | } 53 | 54 | throwException() { 55 | throw new UnauthorizedException(); 56 | } 57 | 58 | addToWhitelist(messages: string[]) { 59 | messages.forEach((message) => this.whitelistedErrors.add(message)); 60 | } 61 | 62 | removeFromWhitelist(messages: PassportWhitelistedErrors[]) { 63 | messages.forEach((message) => this.whitelistedErrors.delete(String(message))); 64 | } 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /src/modules/guards/permissions.guard.ts: -------------------------------------------------------------------------------- 1 | import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; 2 | import { Reflector } from '@nestjs/core'; 3 | 4 | @Injectable() 5 | export class PermissionsGuard implements CanActivate { 6 | constructor(private readonly reflector: Reflector) { } 7 | 8 | canActivate(context: ExecutionContext): boolean { 9 | const handler = context.getHandler(); 10 | const permissions = this.reflector.get('permissions', handler); 11 | if (!permissions) { 12 | return true; 13 | } 14 | 15 | const req = context.switchToHttp().getRequest(); 16 | const user = req.user; 17 | 18 | const hasPermission = () => user.permissions.some(permission => permissions.includes(permission)); 19 | 20 | return user && user.permissions && hasPermission(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/modules/index.ts: -------------------------------------------------------------------------------- 1 | export * from './classes'; 2 | export * from './constants'; 3 | export * from './decorators'; 4 | export * from './dtos'; 5 | export * from './enums'; 6 | export * from './exceptions'; 7 | export * from './filters'; 8 | export * from './functions'; 9 | export * from './guards'; 10 | export * from './interfaces'; 11 | export * from './modules'; 12 | export * from './pipes'; 13 | export * from './providers'; 14 | export * from './services'; 15 | export * from './types'; 16 | export * from './utility'; 17 | export * from './validators'; 18 | export * from './application-tokens.const'; 19 | -------------------------------------------------------------------------------- /src/modules/interfaces/activemq-microservice-context.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ActivemqMicroserviceContext { 2 | rpcType: 'ACTIVE_MQ', 3 | pattern: string; 4 | namespace?: string; 5 | } -------------------------------------------------------------------------------- /src/modules/interfaces/breadcrum.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Breadcrum { 2 | message: string; 3 | category?: string; 4 | data?: any; 5 | } 6 | -------------------------------------------------------------------------------- /src/modules/interfaces/hive-api-docs-configuration.interface.ts: -------------------------------------------------------------------------------- 1 | export interface HiveApiDocConfig { 2 | summary: string; 3 | description: string; 4 | deprecated?: boolean; 5 | response: { 6 | status: number, 7 | type?: any 8 | }; 9 | headers?: { 10 | name: string; 11 | description: string; 12 | }[]; 13 | } 14 | -------------------------------------------------------------------------------- /src/modules/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './activemq-microservice-context.interface'; 2 | export * from './breadcrum.interface'; 3 | export * from './hive-api-docs-configuration.interface'; 4 | export * from './pagination-options.interface'; 5 | export * from './sequelize-error.interface'; 6 | -------------------------------------------------------------------------------- /src/modules/interfaces/pagination-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { SortDirection } from '../types'; 2 | 3 | export interface PaginationOptions { 4 | page?: number; 5 | size?: number; 6 | shift?: number; 7 | sortByModel?: any; 8 | sortBy?: string; 9 | sortDir?: SortDirection; 10 | filter?: any; 11 | search?: string; 12 | user?: any; 13 | } 14 | -------------------------------------------------------------------------------- /src/modules/interfaces/sequelize-error.interface.ts: -------------------------------------------------------------------------------- 1 | export interface SequelizeError { 2 | name: string; 3 | message: string; 4 | stack: string; 5 | original: any; 6 | parent: any; 7 | sql: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/modules/modules/error-handler.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, Module } from '@nestjs/common'; 2 | import { ErrorHandlerConfigurationOptions, getErrorHandlerConfigurationProvider } from '../providers'; 3 | import { ErrorHandler } from '../services'; 4 | 5 | @Module({ 6 | providers: [ 7 | ErrorHandler, 8 | getErrorHandlerConfigurationProvider() 9 | ], 10 | exports: [ 11 | ErrorHandler 12 | ] 13 | }) 14 | export class ErrorHandlerModule { 15 | static register(optionsFactory?: () => ErrorHandlerConfigurationOptions): DynamicModule { 16 | return { 17 | module: ErrorHandlerModule, 18 | providers: [ 19 | ErrorHandler, 20 | getErrorHandlerConfigurationProvider(optionsFactory) 21 | ], 22 | exports: [ErrorHandler] 23 | }; 24 | } 25 | } -------------------------------------------------------------------------------- /src/modules/modules/filter.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | import { FilterProviders } from '../providers'; 3 | 4 | @Global() 5 | @Module({ 6 | providers: [ 7 | ...FilterProviders 8 | ] 9 | }) 10 | export class FilterModule {} 11 | -------------------------------------------------------------------------------- /src/modules/modules/index.ts: -------------------------------------------------------------------------------- 1 | export * from './error-handler.module'; 2 | export * from './filter.module'; 3 | export * from './redis.module'; 4 | -------------------------------------------------------------------------------- /src/modules/modules/redis.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, Module } from '@nestjs/common'; 2 | import { ClientOpts } from 'redis'; 3 | import { RedisProvider } from '../providers'; 4 | import { getRedisConfigurationProvider } from '../providers/redis-configuration/redis-configuration.provider'; 5 | import { RedisService } from '../services'; 6 | 7 | @Module({ 8 | providers: [ 9 | RedisProvider, 10 | RedisService, 11 | getRedisConfigurationProvider() 12 | ], 13 | exports: [ 14 | RedisService 15 | ] 16 | }) 17 | export class RedisModule { 18 | static register(optionsFactory?: () => ClientOpts): DynamicModule { 19 | return { 20 | module: RedisModule, 21 | providers: [ 22 | RedisProvider, 23 | RedisService, 24 | getRedisConfigurationProvider(optionsFactory) 25 | ], 26 | exports: [ 27 | RedisService 28 | ] 29 | }; 30 | } 31 | } -------------------------------------------------------------------------------- /src/modules/pipes/identity-array-validation.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, PipeTransform } from '@nestjs/common'; 2 | import { isArray, isUUID } from 'class-validator'; 3 | import { ValidationException } from '../exceptions'; 4 | 5 | @Injectable() 6 | export class IdentityArrayValidationPipe implements PipeTransform { 7 | async transform(values: string[] = []) { 8 | if (!values) { 9 | throw new ValidationException('array of identities is not defined'); 10 | } 11 | if (!isArray(values)) { 12 | throw new ValidationException('invalid array'); 13 | } 14 | values.forEach((value) => { 15 | if (!isUUID(value)) { 16 | throw new ValidationException('invalid uuid list of identities'); 17 | } 18 | }); 19 | return values; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/modules/pipes/identity-validation.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, PipeTransform } from '@nestjs/common'; 2 | import { isUUID } from 'class-validator'; 3 | import { ValidationException } from '../exceptions'; 4 | 5 | @Injectable() 6 | export class IdentityValidationPipe implements PipeTransform { 7 | async transform(value: string) { 8 | if (!isUUID(value)) { 9 | throw new ValidationException('invalid uuid in url param'); 10 | } 11 | return value; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/modules/pipes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './identity-array-validation.pipe'; 2 | export * from './identity-validation.pipe'; 3 | export * from './validation.pipe'; 4 | -------------------------------------------------------------------------------- /src/modules/pipes/validation.pipe.ts: -------------------------------------------------------------------------------- 1 | import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'; 2 | import { validate } from 'class-validator'; 3 | import { ConstructedObject } from '../classes'; 4 | import { DO_NOT_VALIDATE } from '../constants'; 5 | import { throwValidationErrors } from '../utility'; 6 | 7 | @Injectable() 8 | export class ValidationPipe implements PipeTransform { 9 | async transform(value, metadata: ArgumentMetadata) { 10 | const { metatype, data } = metadata; 11 | if (!metatype || (!this.toValidate(metatype) && !(value instanceof ConstructedObject))) { 12 | return value; 13 | } 14 | 15 | const markedCritical = Reflect.getMetadata('markedCritical', metatype); 16 | 17 | // if the parameter is an injected metadata parameter 18 | if (data === DO_NOT_VALIDATE) { 19 | return value; 20 | } 21 | // Construct the class with the value if has not already been constructed 22 | const object = value instanceof ConstructedObject ? value.object : new metatype(value); 23 | const errors = await validate(object); 24 | if (errors.length > 0) { 25 | throwValidationErrors(errors, markedCritical); 26 | } 27 | 28 | // We will return the constructed class 29 | return object; 30 | } 31 | 32 | private toValidate(metatype): boolean { 33 | const types = [String, Boolean, Number, Array, Object]; 34 | return !types.find((type) => metatype === type); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/modules/providers/error-handler-configuration/error-handler-configuration.provider.ts: -------------------------------------------------------------------------------- 1 | import { FactoryProvider } from '@nestjs/common'; 2 | import { StaticErrorHandlerConfiguration } from '@teammaestro/node-common'; 3 | 4 | export type ErrorHandlerConfigurationOptions = StaticErrorHandlerConfiguration; 5 | export const ErrorHandlerConfigurationToken = 'teamhive:nestjs:ErrorHandlerConfigurationToken'; 6 | 7 | export function getErrorHandlerConfigurationProvider( 8 | optionsFactory: () => ErrorHandlerConfigurationOptions = () => ({ 9 | useSentry: true 10 | }) 11 | ): FactoryProvider { 12 | return { 13 | provide: ErrorHandlerConfigurationToken, 14 | useFactory: optionsFactory 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/modules/providers/error-handler-configuration/index.ts: -------------------------------------------------------------------------------- 1 | export * from './error-handler-configuration.provider'; 2 | -------------------------------------------------------------------------------- /src/modules/providers/filter/filter.provider.ts: -------------------------------------------------------------------------------- 1 | import { ClassProvider } from '@nestjs/common/interfaces'; 2 | import { APP_FILTER } from '@nestjs/core'; 3 | import { LoggedHttpExceptionFilter, PassiveHttpExceptionFilter, RedirectHttpExceptionFilter, UncaughtExceptionFilter } from '../../filters'; 4 | 5 | export const FilterProviders: ClassProvider[] = [ 6 | { 7 | provide: APP_FILTER, 8 | useClass: UncaughtExceptionFilter 9 | }, 10 | { 11 | provide: APP_FILTER, 12 | useClass: PassiveHttpExceptionFilter 13 | }, 14 | { 15 | provide: APP_FILTER, 16 | useClass: RedirectHttpExceptionFilter 17 | }, 18 | { 19 | provide: APP_FILTER, 20 | useClass: LoggedHttpExceptionFilter 21 | } 22 | ]; 23 | -------------------------------------------------------------------------------- /src/modules/providers/filter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './filter.provider'; 2 | -------------------------------------------------------------------------------- /src/modules/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './error-handler-configuration'; 2 | export * from './filter'; 3 | export * from './logger'; 4 | export * from './redis'; 5 | export * from './redis-configuration'; 6 | -------------------------------------------------------------------------------- /src/modules/providers/logger/index.ts: -------------------------------------------------------------------------------- 1 | export * from './logger.provider'; 2 | -------------------------------------------------------------------------------- /src/modules/providers/logger/logger.provider.ts: -------------------------------------------------------------------------------- 1 | import * as log from 'log4js'; 2 | import { ApplicationTokens } from '../../application-tokens.const'; 3 | 4 | export const LoggerProvider = { 5 | provide: ApplicationTokens.LoggerToken, 6 | useFactory: () => { 7 | const logger = log.getLogger(); 8 | return logger; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/modules/providers/redis-configuration/index.ts: -------------------------------------------------------------------------------- 1 | export * from './redis-configuration.provider'; 2 | -------------------------------------------------------------------------------- /src/modules/providers/redis-configuration/redis-configuration.provider.ts: -------------------------------------------------------------------------------- 1 | import { FactoryProvider } from '@nestjs/common'; 2 | import { ClientOpts } from 'redis'; 3 | export const RedisConfigurationToken = 'teamhive:nestjs:RedisConfigurationToken'; 4 | 5 | export type RedisConfigurationOptions = ClientOpts & {expiration?: number; keyPrefix?: string}; 6 | 7 | export function getRedisConfigurationProvider(optionsFactory: () => RedisConfigurationOptions = () => ({ 8 | host: 'localhost' 9 | })): FactoryProvider { 10 | return { 11 | provide: RedisConfigurationToken, 12 | useFactory: optionsFactory 13 | }; 14 | } -------------------------------------------------------------------------------- /src/modules/providers/redis/index.ts: -------------------------------------------------------------------------------- 1 | export * from './redis.provider'; 2 | -------------------------------------------------------------------------------- /src/modules/providers/redis/redis.provider.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@nestjs/common'; 2 | import * as EventEmitter from 'events'; 3 | import * as redis from 'redis'; 4 | import { ApplicationTokens } from '../../application-tokens.const'; 5 | import { RedisConfigurationOptions, RedisConfigurationToken } from '../redis-configuration/redis-configuration.provider'; 6 | 7 | @Injectable() 8 | export class RedisClient extends EventEmitter { 9 | connection: redis.RedisClient; 10 | 11 | private pingRate: number; 12 | private maxTotalRetryTime: number; 13 | 14 | constructor( 15 | @Inject(RedisConfigurationToken) 16 | private readonly redisConfiguration: RedisConfigurationOptions 17 | ) { 18 | super(); 19 | 20 | this.pingRate = 60000; // in ms 21 | this.maxTotalRetryTime = 10000; // in ms 22 | 23 | // create the initial client connection 24 | this.connection = this.setupClient(); 25 | // start pinging redis 26 | this.pingRedis(); 27 | } 28 | 29 | /** 30 | * 1. Create the new client 31 | * 2. Set the retry_strategy to destroy this connection and create a new one if 32 | * can't connect for 10 seconds 33 | * 3. Subscribe and emit the new connection's events 34 | * 4. Return the client 35 | */ 36 | setupClient() { 37 | // initial connection 38 | const self = this; 39 | const client = redis.createClient({ 40 | retry_strategy(options: any) { 41 | if (options.totalRetryTime > self.maxTotalRetryTime) { 42 | self.emit('error', new Error('Retry time exhausted, creating new client connection...')); 43 | 44 | // cleanly end connection, then setup new client connection 45 | client.quit(); 46 | self.connection = self.setupClient(); 47 | return; 48 | } 49 | 50 | // gradually increase time between connection attempts 51 | return Math.min(options.attempt * 100, 3000); 52 | }, 53 | 54 | // default enable_offline_queue to false so that any requests made while client 55 | // is trying to reconnect are immediately sent an error, instead of waiting for 56 | // reconnect and holding up the response to the end user 57 | enable_offline_queue: false, 58 | ...this.redisConfiguration 59 | }); 60 | 61 | this.emitClientEvents(client); 62 | 63 | return client; 64 | } 65 | 66 | /** 67 | * Has the instance of this class listen to and emit the client events. 68 | * This way the application using this RedisClient only has to subscribe to this 69 | * classes events, instead of resubscribing to every new connection created. 70 | * @param client 71 | */ 72 | private emitClientEvents(client: redis.RedisClient) { 73 | client.on('error', error => this.emit('error', error)); 74 | client.on('ready', () => this.emit('ready')); 75 | client.on('reconnecting', () => this.emit('reconnecting')); 76 | client.on('end', () => this.emit('end')); 77 | } 78 | 79 | /** 80 | * Ping redis once every minute to keep the connection from idling 81 | */ 82 | private pingRedis() { 83 | setInterval(() => { 84 | if (this.connection && this.connection.ping) { 85 | this.connection.ping(); 86 | } 87 | }, this.pingRate); 88 | } 89 | } 90 | 91 | export const RedisProvider = { 92 | provide: ApplicationTokens.RedisClientToken, 93 | useClass: RedisClient 94 | }; 95 | -------------------------------------------------------------------------------- /src/modules/services/error-handler/error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable, Optional } from '@nestjs/common'; 2 | import { StaticErrorHandlerConfiguration, StaticErrorHandlerService } from '@teammaestro/node-common'; 3 | import { Logger } from 'log4js'; 4 | import { ApplicationTokens } from '../../application-tokens.const'; 5 | import { Breadcrum } from '../../interfaces/breadcrum.interface'; 6 | import { ErrorHandlerConfigurationToken } from '../../providers/error-handler-configuration/error-handler-configuration.provider'; 7 | @Injectable() 8 | export class ErrorHandler { 9 | constructor( 10 | @Inject(ApplicationTokens.LoggerToken) 11 | private readonly logger: Logger, 12 | @Inject(ErrorHandlerConfigurationToken) 13 | @Optional() 14 | private readonly errorHandlerConfiguration?: StaticErrorHandlerConfiguration 15 | ) { 16 | this.errorHandlerConfiguration = Object.assign( 17 | {}, 18 | { 19 | useSentry: true 20 | }, 21 | errorHandlerConfiguration ?? {} 22 | ); 23 | } 24 | 25 | captureBreadcrumb(breadcrumb: Breadcrum) { 26 | StaticErrorHandlerService.captureBreadcrumb(breadcrumb, this.logger, this.errorHandlerConfiguration); 27 | } 28 | 29 | captureException(error: Error) { 30 | return StaticErrorHandlerService.captureException(error, this.logger, this.errorHandlerConfiguration); 31 | } 32 | 33 | captureMessage( 34 | message: string, 35 | tags?: { 36 | [key: string]: string; 37 | } 38 | ) { 39 | StaticErrorHandlerService.captureMessage(message, this.logger, this.errorHandlerConfiguration, tags); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/modules/services/error-handler/index.ts: -------------------------------------------------------------------------------- 1 | export * from './error-handler.service'; 2 | -------------------------------------------------------------------------------- /src/modules/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './error-handler'; 2 | export * from './redis'; 3 | -------------------------------------------------------------------------------- /src/modules/services/redis/index.ts: -------------------------------------------------------------------------------- 1 | export * from './redis.service'; 2 | -------------------------------------------------------------------------------- /src/modules/services/redis/redis.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@nestjs/common'; 2 | import { ApplicationTokens } from '../../application-tokens.const'; 3 | import { RedisException } from '../../exceptions/redis.exception'; 4 | import { RedisClient } from '../../providers'; 5 | import { RedisConfigurationOptions, RedisConfigurationToken } from '../../providers/redis-configuration/redis-configuration.provider'; 6 | import { ErrorHandler } from '../error-handler'; 7 | 8 | @Injectable() 9 | export class RedisService { 10 | private keyPrefix: string; 11 | private defaultExpiration: number; 12 | 13 | constructor( 14 | @Inject(ApplicationTokens.RedisClientToken) 15 | public readonly client: RedisClient, 16 | @Inject(RedisConfigurationToken) 17 | private readonly redisConfiguration: RedisConfigurationOptions, 18 | 19 | private readonly errorHandler: ErrorHandler 20 | ) { 21 | // tslint:disable 22 | this.client.on('error', error => this.errorHandler.captureException(new RedisException(error))); 23 | this.client.on('ready', () => this.errorHandler.captureBreadcrumb({ message: 'Connected to Redis' })); 24 | this.client.on('reconnecting', () => 25 | this.errorHandler.captureBreadcrumb({ message: 'Attempting to reconnect to Redis...' }) 26 | ); 27 | this.client.on('end', () => 28 | this.errorHandler.captureException(new RedisException(new Error('Redis Connection Fatal'))) 29 | ); 30 | // tslint:enable 31 | 32 | this.defaultExpiration = this.redisConfiguration.expiration ?? 86400; 33 | this.keyPrefix = this.redisConfiguration.keyPrefix ?? ''; 34 | } 35 | 36 | getValue(key: string, ignorePrefix?: boolean) { 37 | return new Promise((resolve, reject) => { 38 | this.client.connection.get(`${ignorePrefix ? '' : this.keyPrefix}${key}`, async (error, response) => { 39 | if (error) { 40 | return reject(error); 41 | } 42 | 43 | let parsedResponse; 44 | try { 45 | parsedResponse = JSON.parse(response); 46 | } catch (error) { 47 | reject(error); 48 | } 49 | 50 | return resolve(parsedResponse); 51 | }); 52 | }); 53 | } 54 | 55 | setValue(key: string, value: any, duration: number = this.defaultExpiration, ignorePrefix?: boolean) { 56 | return new Promise((resolve, reject) => { 57 | if (duration > 0) { 58 | this.client.connection.set( 59 | `${ignorePrefix ? '' : this.keyPrefix}${key}`, 60 | JSON.stringify(value), 61 | 'EX', 62 | duration, 63 | (err, response) => { 64 | if (err) { 65 | return reject(err); 66 | } 67 | return resolve(response); 68 | } 69 | ); 70 | } else { 71 | this.client.connection.set(`${ignorePrefix ? '' : this.keyPrefix}${key}`, JSON.stringify(value), (err, response) => { 72 | if (err) { 73 | return reject(err); 74 | } 75 | return resolve(response); 76 | }); 77 | } 78 | }); 79 | } 80 | 81 | async delete(key: string | string[], ignorePrefix?: boolean) { 82 | if (Array.isArray(key)) { 83 | key = key.map(individualKey => ignorePrefix ? '' : this.keyPrefix + individualKey); 84 | } else { 85 | key = `${ignorePrefix ? '' : this.keyPrefix}${key}`; 86 | } 87 | try { 88 | await this.client.connection.del(key); 89 | } catch (error) { 90 | throw new RedisException(error); 91 | } 92 | } 93 | 94 | async getKeys(pattern: string, ignorePrefix?: boolean) { 95 | return new Promise((resolve, reject) => { 96 | this.client.connection.keys(`${ignorePrefix ? '' : this.keyPrefix}${pattern}`, (err, keys) => { 97 | if (err) { 98 | reject(err) 99 | } else { 100 | resolve(keys) 101 | } 102 | }) 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/modules/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sort-direction.type'; 2 | -------------------------------------------------------------------------------- /src/modules/types/sort-direction.type.ts: -------------------------------------------------------------------------------- 1 | export type SortDirection = 'ASC' | 'DESC'; 2 | -------------------------------------------------------------------------------- /src/modules/utility/index.ts: -------------------------------------------------------------------------------- 1 | export * from './throw-validation-errors'; 2 | -------------------------------------------------------------------------------- /src/modules/utility/throw-validation-errors.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from 'class-validator'; 2 | import { ValidationException } from '../exceptions'; 3 | 4 | export const throwValidationErrors = (errors: ValidationError[], markedCritical = false) => { 5 | const constraints = errors[0].constraints; 6 | const children = errors[0].children; 7 | if (constraints) { 8 | const message = constraints[Object.keys(constraints)[0]]; 9 | throw new ValidationException(message, { critical: markedCritical }); 10 | } else if (children) { 11 | throwValidationErrors(children, markedCritical); 12 | } else { 13 | throw new ValidationException('Validation Failed', { critical: markedCritical }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/modules/validators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './is-number.validator'; 2 | -------------------------------------------------------------------------------- /src/modules/validators/is-number.validator.ts: -------------------------------------------------------------------------------- 1 | import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator'; 2 | 3 | @ValidatorConstraint() 4 | export class IsNumberConstraint implements ValidatorConstraintInterface { 5 | 6 | validate(number: any) { 7 | return !isNaN(number); 8 | } 9 | 10 | defaultMessage(args: ValidationArguments) { 11 | return `$property must be a number.`; 12 | } 13 | 14 | } 15 | 16 | export function IsNumber(validationOptions?: ValidationOptions) { 17 | // tslint:disable-next-line:ban-types 18 | return (object: Object, propertyName: string) => { 19 | registerDecorator({ 20 | target: object.constructor, 21 | propertyName, 22 | options: validationOptions, 23 | constraints: [], 24 | validator: IsNumberConstraint 25 | }); 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2017", 5 | "noImplicitAny": false, 6 | "sourceMap": false, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "rootDir": "src", 10 | "outDir": "", 11 | "declaration": true, 12 | "moduleResolution": "node", 13 | "noUnusedLocals": true, 14 | "removeComments": true 15 | }, 16 | "exclude": [ 17 | "node_modules", 18 | "*.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": { 7 | "no-unused-expression": true 8 | }, 9 | "rules": { 10 | "eofline": false, 11 | "quotemark": [ 12 | true, 13 | "single" 14 | ], 15 | "ordered-imports": [ 16 | false 17 | ], 18 | "max-line-length": [ 19 | 150 20 | ], 21 | "member-ordering": [ 22 | false 23 | ], 24 | "curly": false, 25 | "interface-name": [ 26 | false 27 | ], 28 | "array-type": [ 29 | false 30 | ], 31 | "member-access": [ 32 | false 33 | ], 34 | "no-empty-interface": false, 35 | "no-empty": false, 36 | "arrow-parens": false, 37 | "object-literal-sort-keys": false, 38 | "no-unused-expression": false, 39 | "no-unused-variable": true, 40 | "max-classes-per-file": [ 41 | false 42 | ], 43 | "variable-name": [ 44 | false 45 | ], 46 | "one-line": [ 47 | false 48 | ], 49 | "one-variable-per-declaration": [ 50 | false 51 | ], 52 | "trailing-comma": false, 53 | "no-string-literal": false 54 | }, 55 | "rulesDirectory": [] 56 | } 57 | --------------------------------------------------------------------------------