From bdd46526a18784151ea0ce8ab9bef6e61d14993b Mon Sep 17 00:00:00 2001 From: Livio Brunner Date: Fri, 2 Nov 2018 15:37:47 +0100 Subject: [PATCH] feat(database): Add database health indicator --- .travis.yml | 17 +- Dockerfile | 11 + docker-compose.yml | 12 + e2e/health-checks/database.health.e2e-spec.ts | 99 +++ e2e/jest-e2e.json | 3 +- e2e/terminus.e2e-spec.ts | 4 +- jest.json | 39 +- .../database/connection-not-found.error.ts | 7 + .../database/database.health.ts | 73 ++ .../database/timeout-error.ts | 7 + lib/health-indicators/index.ts | 2 + lib/index.ts | 1 + lib/interfaces/health-indicator.interface.ts | 18 +- lib/terminus-bootstrap.service.ts | 4 +- lib/terminus-core.module.ts | 5 + lib/terminus.module.ts | 3 + lib/utils/index.ts | 1 + lib/utils/promise-timeout.ts | 13 + package-lock.json | 643 ++++++++++++++++++ package.json | 8 +- sample/000-dogs-app/src/app.module.ts | 11 +- sample/000-dogs-app/src/cat/cat.health.ts | 6 +- sample/000-dogs-app/src/dog/dog.health.ts | 6 +- sample/000-dogs-app/src/dog/dog.module.ts | 2 +- 24 files changed, 940 insertions(+), 55 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 e2e/health-checks/database.health.e2e-spec.ts create mode 100644 lib/health-indicators/database/connection-not-found.error.ts create mode 100644 lib/health-indicators/database/database.health.ts create mode 100644 lib/health-indicators/database/timeout-error.ts create mode 100644 lib/health-indicators/index.ts create mode 100644 lib/utils/index.ts create mode 100644 lib/utils/promise-timeout.ts diff --git a/.travis.yml b/.travis.yml index 3201d66f0..6817872c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,27 @@ language: node_js +services: + - docker + cache: directories: - "node_modules" node_js: -- "8" -- "9" - "10" + +env: + global: + - "GH_REF=github.com/BrunnerLivio/nest-terminus.git" + before_install: - npm i -g npm@latest + install: - npm install script: - - npm run test + - docker-compose build && docker-compose up deploy: - provider: script # Have to use `&&` because of issue https://github.com/travis-ci/dpl/issues/673 @@ -31,7 +38,3 @@ deploy: node_js: "10" tags: true tag: beta - -env: - global: - - "GH_REF=github.com/BrunnerLivio/nest-terminus.git" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..017021292 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM node:latest + +WORKDIR /usr/src/app + +COPY package*.json ./ + +RUN npm install + +COPY . . + +CMD ["npm", "test"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..6d7ee217b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: "3" + +services: + + lib: + build: + context: . + networks: + - overlay + +networks: + overlay: diff --git a/e2e/health-checks/database.health.e2e-spec.ts b/e2e/health-checks/database.health.e2e-spec.ts new file mode 100644 index 000000000..88171600b --- /dev/null +++ b/e2e/health-checks/database.health.e2e-spec.ts @@ -0,0 +1,99 @@ +import { INestApplication, DynamicModule } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import { TerminusOptions } from '@godaddy/terminus'; +import { + DatabaseHealthIndicator, + TerminusModuleOptions, + TerminusModule, +} from '../../lib'; +import { HTTP_SERVER_REF, NestFactory } from '@nestjs/core'; +import * as http from 'http'; + +import { TypeOrmModule } from '@nestjs/typeorm'; +import Axios from 'axios'; + +describe('Database Health', () => { + let app: INestApplication; + const PORT = process.env.PORT || 3001; + + const getTerminusOptions = ( + db: DatabaseHealthIndicator, + ): TerminusModuleOptions => ({ + endpoints: [ + { + url: '/health', + healthIndicators: [async () => db.pingCheck('database')], + }, + ], + }); + + class ApplicationModule { + static forRoot(options): DynamicModule { + return { + module: ApplicationModule, + imports: [ + TypeOrmModule.forRoot({ + type: 'sqlite', + database: 'test', + keepConnectionAlive: true, + }), + TerminusModule.forRootAsync(options), + ], + }; + } + } + + async function bootstrapModule(options) { + app = await NestFactory.create(ApplicationModule.forRoot(options)); + await app.listen(PORT); + } + + it('should check if the database is available', async () => { + await bootstrapModule({ + inject: [DatabaseHealthIndicator], + useFactory: getTerminusOptions, + }); + + const response = await Axios.get(`http://0.0.0.0:${PORT}/health`); + expect(response.status).toBe(200); + expect(response.data).toEqual({ + status: 'ok', + info: { database: { status: 'up' } }, + }); + }); + + it('should throw an error if runs into timeout error', async () => { + await bootstrapModule({ + inject: [DatabaseHealthIndicator], + useFactory: (db: DatabaseHealthIndicator): TerminusModuleOptions => ({ + endpoints: [ + { + url: '/health', + healthIndicators: [ + async () => db.pingCheck('database', { timeout: 1 }), + ], + }, + ], + }), + }); + + try { + await Axios.get(`http://0.0.0.0:${PORT}/health`, {}); + } catch (error) { + expect(error.response.status).toBe(503); + expect(error.response.data).toEqual({ + status: 'error', + error: { + database: { + status: 'down', + message: 'Database did not respond after 1ms', + }, + }, + }); + } + }); + + afterEach(async () => { + app.close(); + }); +}); diff --git a/e2e/jest-e2e.json b/e2e/jest-e2e.json index 0fba759a7..72f3243f8 100644 --- a/e2e/jest-e2e.json +++ b/e2e/jest-e2e.json @@ -17,5 +17,6 @@ "coverageReporters": [ "json", "lcov" - ] + ], + "testEnvironment": "node" } diff --git a/e2e/terminus.e2e-spec.ts b/e2e/terminus.e2e-spec.ts index b9c928f35..69075414c 100644 --- a/e2e/terminus.e2e-spec.ts +++ b/e2e/terminus.e2e-spec.ts @@ -27,7 +27,9 @@ describe('Terminus', () => { endpoints: [ { url: '/health', - healthIndicators: [async () => ({ key: true })], + healthIndicators: [ + async () => ({ db: { whatever: true, status: 'up' } }), + ], }, ], }; diff --git a/jest.json b/jest.json index ca221d75c..4ddcfb499 100644 --- a/jest.json +++ b/jest.json @@ -1,21 +1,22 @@ { - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "json" - ], - "transform": { - "^.+\\.tsx?$": "/node_modules/ts-jest/preprocessor.js" - }, - "testRegex": "/src/.*\\.(test|spec).(ts|tsx|js)$", - "collectCoverageFrom": [ - "src/**/*.{js,jsx,tsx,ts}", - "!**/node_modules/**", - "!**/vendor/**" - ], - "coverageReporters": [ - "json", - "lcov" - ] + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "transform": { + "^.+\\.tsx?$": "/node_modules/ts-jest/preprocessor.js" + }, + "testRegex": "/src/.*\\.(test|spec).(ts|tsx|js)$", + "collectCoverageFrom": [ + "src/**/*.{js,jsx,tsx,ts}", + "!**/node_modules/**", + "!**/vendor/**" + ], + "coverageReporters": [ + "json", + "lcov" + ], + "testEnvironment": "node" } diff --git a/lib/health-indicators/database/connection-not-found.error.ts b/lib/health-indicators/database/connection-not-found.error.ts new file mode 100644 index 000000000..454d48858 --- /dev/null +++ b/lib/health-indicators/database/connection-not-found.error.ts @@ -0,0 +1,7 @@ +import { HealthCheckError } from '@godaddy/terminus'; + +export class ConnectionNotFoundError extends HealthCheckError { + constructor(cause) { + super('Connection provider not found in application context', cause); + } +} diff --git a/lib/health-indicators/database/database.health.ts b/lib/health-indicators/database/database.health.ts new file mode 100644 index 000000000..c0e507319 --- /dev/null +++ b/lib/health-indicators/database/database.health.ts @@ -0,0 +1,73 @@ +import { Injectable, Optional } from '@nestjs/common'; +import { HealthIndicatorResult } from '../..'; +import { Connection } from 'typeorm'; +import { HealthCheckError } from '@godaddy/terminus'; +import { ConnectionNotFoundError } from './connection-not-found.error'; +import { + promiseTimeout, + TimeoutError as PromiseTimeoutError, +} from '../../utils'; +import { TimeoutError } from './timeout-error'; + +export interface DatabasePingCheckSettings { + connection?: Connection; + timeout?: number; +} + +@Injectable() +export class DatabaseHealthIndicator { + constructor(@Optional() private readonly connection: Connection) {} + + private getStatus(key: string, isHealthy: boolean, options?: any) { + return { + [key]: { + status: isHealthy ? 'up' : 'down', + ...options, + }, + }; + } + + private async pingDb(connection, timeout) { + return await promiseTimeout(timeout, connection.query('SELECT 1')); + } + + async pingCheck( + key: string, + options: DatabasePingCheckSettings = {}, + ): Promise { + let isHealthy = false; + const connection = options.connection || this.connection; + const timeout = options.timeout || 1000; + + if (!connection) { + throw new ConnectionNotFoundError( + this.getStatus(key, isHealthy, { + message: 'Connection provider not found in application context', + }), + ); + } + + try { + await this.pingDb(connection, timeout); + isHealthy = true; + } catch (err) { + if (err instanceof PromiseTimeoutError) { + throw new TimeoutError( + timeout, + this.getStatus(key, isHealthy, { + message: `Database did not respond after ${timeout}ms`, + }), + ); + } + } + + if (isHealthy) { + return this.getStatus(key, isHealthy); + } else { + throw new HealthCheckError( + 'Database is not available', + this.getStatus(key, isHealthy), + ); + } + } +} diff --git a/lib/health-indicators/database/timeout-error.ts b/lib/health-indicators/database/timeout-error.ts new file mode 100644 index 000000000..1434e894d --- /dev/null +++ b/lib/health-indicators/database/timeout-error.ts @@ -0,0 +1,7 @@ +import { HealthCheckError } from '@godaddy/terminus'; + +export class TimeoutError extends HealthCheckError { + constructor(timeout, cause) { + super(`Database did not respond after ${timeout}ms`, cause); + } +} diff --git a/lib/health-indicators/index.ts b/lib/health-indicators/index.ts new file mode 100644 index 000000000..e72c4227e --- /dev/null +++ b/lib/health-indicators/index.ts @@ -0,0 +1,2 @@ +export * from './database/database.health'; +export * from './database/connection-not-found.error'; diff --git a/lib/index.ts b/lib/index.ts index ae453f369..f6d20479e 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,2 +1,3 @@ export * from './interfaces'; export * from './terminus.module'; +export * from './health-indicators'; diff --git a/lib/interfaces/health-indicator.interface.ts b/lib/interfaces/health-indicator.interface.ts index cf9b2e4df..92d32facf 100644 --- a/lib/interfaces/health-indicator.interface.ts +++ b/lib/interfaces/health-indicator.interface.ts @@ -1,18 +1,8 @@ export type HealthIndicatorResult = { - [key: string]: any; + [key: string]: { + status: string; + [optionalKeys: string]: any; + }; }; export type HealthIndicatorFunction = () => Promise; - -/** - * Represents a health indicator of a health check - */ -export interface HealthIndicator { - /** - * If the health indicator is healthy - * - * @param {string} key The key of the health check which will be used in the result object - * @param {any} [options] The options to configure the health indicator - */ - isHealthy(key: string, options?: any): Promise; -} diff --git a/lib/terminus-bootstrap.service.ts b/lib/terminus-bootstrap.service.ts index 5ac9f9ebe..70f8e2b6e 100644 --- a/lib/terminus-bootstrap.service.ts +++ b/lib/terminus-bootstrap.service.ts @@ -5,7 +5,7 @@ import { HttpServer, } from '@nestjs/common'; import { TERMINUS_MODULE_OPTIONS, TERMINUS_LIB } from './terminus.constants'; -import { TerminusModuleOptions, HealthIndicator } from './interfaces'; +import { TerminusModuleOptions } from './interfaces'; import { HTTP_SERVER_REF } from '@nestjs/core'; import { Server } from 'http'; import { HealthCheckError, Terminus } from '@godaddy/terminus'; @@ -44,7 +44,7 @@ export class TerminusBootstrapService implements OnApplicationBootstrap { ): Promise<{ results: any[]; errors: any[] }> { const results: any[] = []; const errors: any[] = []; - await Promise.all( + await Promise.all( healthIndicators // Register all promises .map(healthIndicator => healthIndicator()) diff --git a/lib/terminus-core.module.ts b/lib/terminus-core.module.ts index f5168a524..57af80e97 100644 --- a/lib/terminus-core.module.ts +++ b/lib/terminus-core.module.ts @@ -15,6 +15,7 @@ import { TERMINUS_MODULE_OPTIONS } from './terminus.constants'; import { TerminusBootstrapService } from './terminus-bootstrap.service'; import { TerminusLibProvider } from './terminus-lib.provider'; import { TerminusModule } from './terminus.module'; +import { DatabaseHealthIndicator } from '.'; /** * The internal Terminus Module which handles the integration @@ -49,7 +50,9 @@ export class TerminusCoreModule { terminusModuleOptions, TerminusLibProvider, TerminusBootstrapService, + DatabaseHealthIndicator, ], + exports: [DatabaseHealthIndicator], }; } @@ -67,7 +70,9 @@ export class TerminusCoreModule { ...asyncProviders, TerminusBootstrapService, TerminusLibProvider, + DatabaseHealthIndicator, ], + exports: [DatabaseHealthIndicator], }; } diff --git a/lib/terminus.module.ts b/lib/terminus.module.ts index 0ac98c9e3..44b5520a4 100644 --- a/lib/terminus.module.ts +++ b/lib/terminus.module.ts @@ -4,6 +4,7 @@ import { TerminusModuleAsyncOptions, } from './interfaces/terminus-module-options.interface'; import { TerminusCoreModule } from './terminus-core.module'; +import { DatabaseHealthIndicator } from './health-indicators'; /** * Terminus Module which represents the integration of the @@ -18,6 +19,8 @@ export class TerminusModule { static forRoot(options?: TerminusModuleOptions): DynamicModule { return { module: TerminusModule, + providers: [DatabaseHealthIndicator], + exports: [DatabaseHealthIndicator], imports: [TerminusCoreModule.forRoot(options)], }; } diff --git a/lib/utils/index.ts b/lib/utils/index.ts new file mode 100644 index 000000000..962995cd8 --- /dev/null +++ b/lib/utils/index.ts @@ -0,0 +1 @@ +export * from './promise-timeout'; diff --git a/lib/utils/promise-timeout.ts b/lib/utils/promise-timeout.ts new file mode 100644 index 000000000..dabb2a06c --- /dev/null +++ b/lib/utils/promise-timeout.ts @@ -0,0 +1,13 @@ +export class TimeoutError extends Error {} +export const promiseTimeout = function(ms, promise) { + // Create a promise that rejects in milliseconds + let timeout = new Promise((resolve, reject) => { + let id = setTimeout(() => { + clearTimeout(id); + reject(new TimeoutError('Timed out in ' + ms + 'ms.')); + }, ms); + }); + + // Returns a race between our timeout and the passed in promise + return Promise.race([promise, timeout]); +}; diff --git a/package-lock.json b/package-lock.json index 031014697..114e51ea2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -320,6 +320,15 @@ "optional": "0.1.4" } }, + "@nestjs/typeorm": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-5.2.2.tgz", + "integrity": "sha512-D1xtTYFn+Pbf0yEY+mj3m3AwSyr+J8PW5UPYDLTGTR6gbLQvgOM/R+TddIgU0M0C/jxsvSXEVsVPtWaG6SIlzw==", + "dev": true, + "requires": { + "uuid": "3.3.2" + } + }, "@nodelib/fs.stat": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz", @@ -365,6 +374,12 @@ "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", "dev": true }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -485,6 +500,12 @@ "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", "dev": true }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -521,6 +542,12 @@ "integrity": "sha1-7klza2ObTxCLbp5ibG2pkwa0FpI=", "dev": true }, + "app-root-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz", + "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=", + "dev": true + }, "append-field": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz", @@ -536,6 +563,48 @@ "default-require-extensions": "^1.0.0" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1051,6 +1120,12 @@ } } }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -1185,6 +1260,16 @@ "node-int64": "^0.4.0" } }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -1418,6 +1503,12 @@ } } }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, "ci-info": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", @@ -1533,6 +1624,110 @@ "restore-cursor": "^1.0.1" } }, + "cli-highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-1.2.3.tgz", + "integrity": "sha512-cmc4Y2kJuEpT2KZd9pgWWskpDMMfJu2roIcY1Ya/aIItufF5FKsV/NtA6vvdhSUllR8KJfvQDNmIcskU+MKLDg==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "highlight.js": "^9.6.0", + "mz": "^2.4.0", + "parse5": "^3.0.3", + "yargs": "^10.0.3" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "yargs": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", + "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^8.1.0" + } + }, + "yargs-parser": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", + "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, "cli-truncate": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", @@ -1787,6 +1982,12 @@ "std-env": "^1.1.0" } }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -1984,6 +2185,12 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -2032,6 +2239,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -2059,6 +2272,12 @@ "repeating": "^2.0.0" } }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -2149,6 +2368,12 @@ "integrity": "sha1-xzdwGfxOVQeYkosrmv62ar+h8vk=", "dev": true }, + "dotenv": { + "version": "5.0.1", + "resolved": "http://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz", + "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==", + "dev": true + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -2725,6 +2950,12 @@ "bser": "^2.0.0" } }, + "figlet": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.2.1.tgz", + "integrity": "sha512-qc8gycfnnfOmfvPl7Fi3JeTbcvdmbZkckyUVGGAM02je7Ookvu+bBfKy1I4FKqTsQHCs3ARJ76ip/k98r+OQuQ==", + "dev": true + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -2912,6 +3143,15 @@ "universalify": "^0.1.0" } }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3453,6 +3693,53 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -3675,6 +3962,12 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -3735,6 +4028,12 @@ } } }, + "highlight.js": { + "version": "9.13.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz", + "integrity": "sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==", + "dev": true + }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -3890,12 +4189,27 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, "ignore": { "version": "3.3.10", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, "import-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", @@ -3988,6 +4302,12 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "inquirer": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.0.6.tgz", @@ -6244,6 +6564,39 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "dev": true + } + } + }, + "minizlib": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.1.tgz", + "integrity": "sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", @@ -6346,6 +6699,17 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "nan": { "version": "2.11.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", @@ -6399,6 +6763,28 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "needle": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", + "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", + "dev": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -6435,6 +6821,34 @@ "which": "^1.3.0" } }, + "node-pre-gyp": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", + "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", + "dev": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -6456,6 +6870,22 @@ "remove-trailing-separator": "^1.0.1" } }, + "npm-bundled": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", + "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==", + "dev": true + }, + "npm-packlist": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz", + "integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, "npm-path": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz", @@ -6485,6 +6915,18 @@ "which": "^1.2.10" } }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -6770,6 +7212,16 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -6806,6 +7258,12 @@ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", "dev": true }, + "parent-require": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", + "integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=", + "dev": true + }, "parse-glob": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", @@ -7118,6 +7576,26 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "read-pkg": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", @@ -7948,6 +8426,25 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sqlite3": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.3.tgz", + "integrity": "sha512-nuWqc26oiJZXyY5MEz+rQbiki1BTibnXsy8Kqo7QD/ut6eksOWi6uWwFMbdnFNME7CZyplWdDXj2fbdQVaEfuA==", + "dev": true, + "requires": { + "nan": "~2.10.0", + "node-pre-gyp": "^0.10.3", + "request": "^2.87.0" + }, + "dependencies": { + "nan": { + "version": "2.10.0", + "resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "dev": true + } + } + }, "sshpk": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", @@ -8173,6 +8670,12 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, "superagent": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", @@ -8248,6 +8751,35 @@ "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true }, + "tar": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz", + "integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==", + "dev": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "dev": true + } + } + }, "test-exclude": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.3.tgz", @@ -8299,6 +8831,24 @@ } } }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, "throat": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", @@ -8626,6 +9176,27 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typeorm": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.8.tgz", + "integrity": "sha512-MvMwi3Qd1nTBMZsohOy9Pb7eHFXY+YRtPdG90ayfPeC7z2DJXE0gOF8zAKUlxLy1+Dyyq/tOQS+LY/4/6yI/3A==", + "dev": true, + "requires": { + "app-root-path": "^2.0.1", + "buffer": "^5.1.0", + "chalk": "^2.3.2", + "cli-highlight": "^1.2.3", + "debug": "^3.1.0", + "dotenv": "^5.0.1", + "glob": "^7.1.2", + "js-yaml": "^3.11.0", + "mkdirp": "^0.5.1", + "reflect-metadata": "^0.1.12", + "xml2js": "^0.4.17", + "yargonaut": "^1.1.2", + "yargs": "^11.1.0" + } + }, "typescript": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", @@ -8971,6 +9542,15 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, "win-release": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz", @@ -9066,6 +9646,22 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", @@ -9084,6 +9680,53 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, + "yargonaut": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz", + "integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "figlet": "^1.1.1", + "parent-require": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "yargs": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", diff --git a/package.json b/package.json index de4f515d9..c4e6a76b2 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "@brunnerlivio/terminus", - "version": "5.4.0", + "version": "5.4.0-rc2", "description": "Nest - modern, fast, powerful node.js web framework (@terminus)", "author": "Kamil Mysliwiec", + "main": "dist/index.js", "license": "MIT", "scripts": { "build": "rm -rf dist && tsc -p tsconfig.json", @@ -22,6 +23,7 @@ "@nestjs/common": "^5.4.0", "@nestjs/core": "^5.4.0", "@nestjs/testing": "^5.4.0", + "@nestjs/typeorm": "^5.2.2", "@types/jest": "^23.3.9", "@types/node": "^8.9.0", "husky": "^1.1.3", @@ -31,10 +33,12 @@ "reflect-metadata": "0.1.12", "rxjs": "^6.3.3", "rxjs-compat": "^6.3.3", + "sqlite3": "^4.0.3", "supertest": "^3.3.0", "ts-jest": "^23.10.4", "ts-node": "^7.0.1", "tslint-config-prettier": "^1.15.0", + "typeorm": "^0.2.8", "typescript": "^3.1.6" }, "repository": { @@ -43,6 +47,8 @@ "peerDependencies": { "@nestjs/common": "^5.4.0", "@nestjs/core": "^5.4.0", + "@nestjs/typeorm": "^5.2.2", + "typeorm": "^0.2.8", "@godaddy/terminus": "^4.1.0", "reflect-metadata": "0.1.12", "rxjs": "^6.3.3" diff --git a/sample/000-dogs-app/src/app.module.ts b/sample/000-dogs-app/src/app.module.ts index 5594dd8a9..b47167ba7 100644 --- a/sample/000-dogs-app/src/app.module.ts +++ b/sample/000-dogs-app/src/app.module.ts @@ -1,12 +1,17 @@ import { Module, DynamicModule } from '@nestjs/common'; import { DogModule } from './dog/dog.module'; import { CatModule } from './cat/cat.module'; -import { TerminusModule, TerminusModuleOptions } from '../../../lib'; -import { DogHealthIndicator } from './dog/dog.healthcheck'; +import { + TerminusModule, + TerminusModuleOptions, + DatabaseHealthIndicator, +} from '../../../lib'; +import { DogHealthIndicator } from './dog/dog.health'; +import { CatHealthIndicator } from './cat/cat.health'; const getTerminusOptions = ( dogHealthIndicator: DogHealthIndicator, - catHealthIndicator: DogHealthIndicator, + catHealthIndicator: CatHealthIndicator, ): TerminusModuleOptions => ({ endpoints: [ { diff --git a/sample/000-dogs-app/src/cat/cat.health.ts b/sample/000-dogs-app/src/cat/cat.health.ts index 7d90e1924..b2dee4fb3 100644 --- a/sample/000-dogs-app/src/cat/cat.health.ts +++ b/sample/000-dogs-app/src/cat/cat.health.ts @@ -1,10 +1,10 @@ import { Injectable } from '@nestjs/common'; -import { HealthIndicator } from '../../../../lib'; import { HealthCheckError } from '@godaddy/terminus'; +import { HealthIndicatorResult } from '../../../../lib'; @Injectable() -export class CatHealthIndicator implements HealthIndicator { - async isHealthy(key: string) { +export class CatHealthIndicator { + async isHealthy(key: string): Promise { const isHealthy = true; const status = { diff --git a/sample/000-dogs-app/src/dog/dog.health.ts b/sample/000-dogs-app/src/dog/dog.health.ts index 758264504..7079e4b78 100644 --- a/sample/000-dogs-app/src/dog/dog.health.ts +++ b/sample/000-dogs-app/src/dog/dog.health.ts @@ -2,13 +2,13 @@ import { Injectable } from '@nestjs/common'; import { DogService } from './dog.service'; import { DogState } from './dog.interface'; import { HealthCheckError } from '@godaddy/terminus'; -import { HealthIndicator } from '../../../../lib'; +import { HealthIndicatorResult } from '../../../../lib'; @Injectable() -export class DogHealthIndicator implements HealthIndicator { +export class DogHealthIndicator { constructor(private readonly dogService: DogService) {} - async isHealthy(key: string): Promise { + async isHealthy(key: string): Promise { const dogs = await this.dogService.getDogs(); const goodboys = dogs.filter(dog => dog.state === DogState.GOOD_BOY); const badboys = dogs.filter(dog => dog.state === DogState.BAD_BOY); diff --git a/sample/000-dogs-app/src/dog/dog.module.ts b/sample/000-dogs-app/src/dog/dog.module.ts index 1d294cabc..fc581e5e3 100644 --- a/sample/000-dogs-app/src/dog/dog.module.ts +++ b/sample/000-dogs-app/src/dog/dog.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { DogService } from './dog.service'; -import { DogHealthIndicator } from './dog.healthcheck'; +import { DogHealthIndicator } from './dog.health'; @Module({ providers: [DogService, DogHealthIndicator],