├── src ├── runner │ ├── index.ts │ └── runner.ts ├── structs │ ├── index.ts │ └── response.ts ├── log │ ├── index.ts │ ├── stub.ts │ └── pino.ts ├── redis │ ├── index.ts │ ├── factory.ts │ └── client.ts ├── config │ ├── index.ts │ ├── constants.ts │ └── app_config.ts ├── meter │ ├── index.ts │ ├── stub.ts │ ├── statsd.ts │ └── meter.ts ├── ids │ ├── index.ts │ ├── round_counter.ts │ ├── snow_flake.ts │ └── the-ids.ts ├── rpc │ ├── index.ts │ ├── constants.ts │ ├── status.ts │ ├── cache.ts │ ├── adapter │ │ └── redis.ts │ └── agnostic.ts ├── types │ ├── index.ts │ ├── band.ts │ ├── config.ts │ ├── log.ts │ ├── meter.ts │ ├── redis.ts │ └── rpc.ts └── index.ts ├── .gitignore ├── .bumpversion.cfg ├── @types ├── flake-idgen │ └── index.d.ts ├── cctz │ └── index.d.ts ├── merge-options │ └── index.d.ts ├── xxhash │ └── index.d.ts └── redis-fast-driver │ └── index.d.ts ├── .editorconfig ├── Dockerfile ├── README.md ├── tsconfig.json ├── makefile ├── package.json ├── .travis.yml ├── LICENSE └── yarn.lock /src/runner/index.ts: -------------------------------------------------------------------------------- 1 | export * from './runner'; 2 | -------------------------------------------------------------------------------- /src/structs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './response'; 2 | -------------------------------------------------------------------------------- /src/log/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pino'; 2 | export * from './stub'; 3 | -------------------------------------------------------------------------------- /src/redis/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client' 2 | export * from './factory' 3 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app_config'; 2 | export * from './constants'; 3 | -------------------------------------------------------------------------------- /src/meter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './statsd' 2 | export * from './meter' 3 | export * from './stub' 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /node_modules 3 | /yarn-error.log 4 | /deploy 5 | /.env* 6 | /dist 7 | /emergency 8 | -------------------------------------------------------------------------------- /src/ids/index.ts: -------------------------------------------------------------------------------- 1 | export * from './round_counter'; 2 | export * from './the-ids'; 3 | export * from './snow_flake'; 4 | -------------------------------------------------------------------------------- /src/config/constants.ts: -------------------------------------------------------------------------------- 1 | export const ENV_DEV = 'dev'; 2 | export const ENV_PROD = 'production'; 3 | export const ENV_STAGE = 'stage'; 4 | -------------------------------------------------------------------------------- /src/rpc/index.ts: -------------------------------------------------------------------------------- 1 | export * from './agnostic' 2 | export * from './adapter/redis' 3 | export * from './constants' 4 | export * from './status' 5 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './meter'; 2 | export * from './redis'; 3 | export * from './rpc'; 4 | export * from './log' 5 | export * from './config'; 6 | export * from './band'; 7 | -------------------------------------------------------------------------------- /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.8.5 3 | commit = True 4 | tag = False 5 | 6 | [bumpversion:file:package.json] 7 | 8 | [bumpversion:file:Dockerfile] 9 | 10 | [bumpversion:file:src/index.ts] 11 | 12 | -------------------------------------------------------------------------------- /@types/flake-idgen/index.d.ts: -------------------------------------------------------------------------------- 1 | 2 | /// 3 | 4 | declare module 'flake-idgen' { 5 | 6 | class FlakeId { 7 | constructor(options?: any); 8 | next(cb?: (id: Buffer) => any): Buffer; 9 | } 10 | 11 | namespace FlakeId { } 12 | export = FlakeId; 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ids'; 2 | export * from './meter'; 3 | export * from './rpc'; 4 | export * from './types'; 5 | export * from './log'; 6 | export * from './redis'; 7 | export * from './config'; 8 | export * from './structs'; 9 | // export * from './runner'; 10 | export const version = '1.8.5'; -------------------------------------------------------------------------------- /@types/cctz/index.d.ts: -------------------------------------------------------------------------------- 1 | // TypeScript Version: 2.7 2 | 3 | /// 4 | 5 | declare module 'cctz' //{ 6 | 7 | // interface cctz { 8 | // format(format: string, unix: number, timezone?: string): string; 9 | // } 10 | 11 | // namespace cctz { } 12 | // export = cctz; 13 | // } 14 | -------------------------------------------------------------------------------- /src/rpc/constants.ts: -------------------------------------------------------------------------------- 1 | export const SERVICE_DIRECTOR = 'director'; 2 | export const SERVICE_KERNEL = 'kernel'; 3 | export const SERVICE_TRACK = 'track'; 4 | 5 | export const METHOD_IAMALIVE = '__iamalive'; 6 | export const METHOD_STATUS = '__status'; 7 | 8 | export const BROADCAST = 'broadcast'; 9 | 10 | 11 | 12 | // ### Orchastration 13 | 14 | export const STATUS_RUNNING = 'running'; 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /src/meter/stub.ts: -------------------------------------------------------------------------------- 1 | import { MeterFacade } from '../types'; 2 | 3 | export class StubMeter implements MeterFacade { 4 | tick(metric: string, tags?: { [k: string]: string | number }): void { } 5 | timenote(metric: string, tags?: { [k: string]: string | number }): () => number { 6 | return () => 0; 7 | } 8 | time(metric: string, duration: number, tags?: { [k: string]: string | number }): void { } 9 | } 10 | -------------------------------------------------------------------------------- /src/redis/factory.ts: -------------------------------------------------------------------------------- 1 | import { RedisClient } from "../redis"; 2 | import { RedisClientOptions, RedisFactoryType } from "../types"; 3 | 4 | 5 | export class RedisFactory implements RedisFactoryType { 6 | options: RedisClientOptions; 7 | 8 | constructor(options: RedisClientOptions) { 9 | this.options = options; 10 | } 11 | 12 | create(): RedisClient { 13 | return new RedisClient(this.options) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ids/round_counter.ts: -------------------------------------------------------------------------------- 1 | 2 | const DEFAULT_SIZE = 99999; 3 | 4 | export class IdGenRoundCounter { 5 | num: number; 6 | size: number; 7 | 8 | constructor(size: number = DEFAULT_SIZE) { 9 | this.num = Math.round(Math.random() * size); 10 | this.size = size; 11 | } 12 | 13 | take(): number { 14 | if (this.num === this.size) { 15 | this.num = 0; 16 | } 17 | return ++this.num; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8-alpine 2 | 3 | LABEL maintainer="Dmitry Rodin " 4 | LABEL band.base-ts.version="1.8.5" 5 | 6 | ENV TZ UTC 7 | ENV LOG_LEVEL warn 8 | 9 | RUN apk add python --no-cache make build-base gcc git curl 10 | 11 | WORKDIR /usr/src/rockme 12 | 13 | COPY package.json . 14 | COPY yarn.lock . 15 | 16 | RUN yarn install \ 17 | && yarn cache clean 18 | 19 | COPY . . 20 | 21 | RUN yarn build && yarn link 22 | -------------------------------------------------------------------------------- /src/ids/snow_flake.ts: -------------------------------------------------------------------------------- 1 | import * as FlakeIdGen from 'flake-idgen'; 2 | import { Uint64BE } from 'int64-buffer' 3 | 4 | export class IdGenShowFlake { 5 | 6 | idGen: FlakeIdGen; 7 | 8 | constructor() { 9 | this.idGen = new FlakeIdGen(); 10 | } 11 | 12 | take(): string { 13 | const idBuff = this.idGen.next(); 14 | return new Uint64BE(idBuff).toString(); 15 | } 16 | 17 | withTime() { 18 | return { 19 | id: this.take(), 20 | 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /@types/merge-options/index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for merge-options 2 | // Project: 3 | // Definitions by: Dmitry Rodin 4 | // TypeScript Version: 2.8 5 | 6 | declare module 'merge-options' { 7 | 8 | // type anyobj = { [key: string]: string }; 9 | type OptionsType = object 10 | interface Assign { 11 | (...obj: OptionsType[]): any; 12 | } 13 | const mergeOptions: Assign; 14 | 15 | namespace mergeOptions { } 16 | export = mergeOptions; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/rpc/status.ts: -------------------------------------------------------------------------------- 1 | import { format as dateFormat } from 'cctz'; 2 | import { RPCAppStatus, RequestHandler } from '../types'; 3 | 4 | export class AppStatus { 5 | appStarted: Date = new Date(); 6 | 7 | get: RequestHandler = (params: object): RPCAppStatus => { 8 | const appUptime = Number(new Date()) - Number(this.appStarted); 9 | return { 10 | app_state: "running", 11 | app_started: Number(this.appStarted), 12 | app_uptime: appUptime, 13 | methods: [] 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/log/stub.ts: -------------------------------------------------------------------------------- 1 | import { LoggerType, LogFn, LoggerConfig } from "../types"; 2 | 3 | export class StubLogger implements LoggerType { 4 | debug: LogFn = (...args: any[]) => { }; 5 | info: LogFn = (...args: any[]) => { }; 6 | warn: LogFn = (...args: any[]) => { }; 7 | error: LogFn = (...args: any[]) => { }; 8 | trace: LogFn = (...args: any[]) => { }; 9 | fatal: LogFn = (...args: any[]) => { }; 10 | for(fn: Function): LoggerType { 11 | return new StubLogger(); 12 | } 13 | child(options: LoggerConfig): LoggerType { 14 | return new StubLogger(); 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/rpc/cache.ts: -------------------------------------------------------------------------------- 1 | import * as LRU from 'lru-cache'; 2 | 3 | const LRU_DEF_LIMIT = 500; 4 | 5 | export class RPCCache { 6 | 7 | cache: LRU.Cache; 8 | 9 | constructor() { 10 | const options = { 11 | max: LRU_DEF_LIMIT, 12 | maxAge: 5 * 6e4 // 5min 13 | } 14 | this.cache = LRU(options); 15 | } 16 | 17 | /** 18 | * 19 | * @param key string key 20 | * @param val any value 21 | * @param maxAge seconds 22 | */ 23 | set(key: string, val: any, maxAge?: number) { 24 | return this.cache.set(key, val, maxAge ? maxAge * 1e3 : maxAge); 25 | } 26 | 27 | get(key: string) { 28 | return this.cache.get(key); 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/types/band.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export type EnrichersRequirements = Array<[string, string]> 4 | 5 | export interface MethodRegistrationOptions { 6 | alias?: string; 7 | timeout?: number; 8 | props?: { [k: string]: string }; 9 | keys?: Array 10 | } 11 | 12 | export interface MethodRegRequest { 13 | register?: Array 14 | state_hash?: string; 15 | } 16 | 17 | export interface MethodRegistration { 18 | service: string; 19 | method: string; 20 | role: string; 21 | options: MethodRegistrationOptions; 22 | } 23 | 24 | export interface RPCAppStatus { 25 | app_state: "running", 26 | app_started: number; 27 | app_uptime: number; 28 | methods: Array 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/types/config.ts: -------------------------------------------------------------------------------- 1 | export type Envs = 'dev' | 'production' | 'stage'; 2 | 3 | import { RPCConfig } from './rpc' 4 | import { RedisConfig } from './redis' 5 | import { LoggerConfig } from './log' 6 | import { MeterConfig } from './meter'; 7 | 8 | export type AnyConfig = { [k: string]: any }; 9 | 10 | 11 | export type AbstractConfig = T & ImplicitConfig & AnyConfig; 12 | 13 | export type ConfigRoot = ImplicitConfig & AnyConfig; 14 | 15 | export interface ImplicitConfig { 16 | env: Envs; 17 | name: string; 18 | redis: RedisConfig; 19 | log: LoggerConfig; 20 | metrics: MeterConfig; 21 | rpc: RPCConfig; 22 | } 23 | 24 | export interface AppConfigOptions { 25 | dir?: string; 26 | vars?: { [k: string]: any }; 27 | } 28 | -------------------------------------------------------------------------------- /src/types/log.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface PinoConfig { 3 | name?: string; 4 | safe?: boolean; 5 | level?: string; 6 | prettyPrint?: boolean; 7 | } 8 | 9 | export interface LoggerConfig { 10 | pino: PinoConfig; 11 | logger?: LoggerType; 12 | } 13 | 14 | export interface LogFn { 15 | (msg: string, ...args: any[]): void; 16 | (obj: object, msg?: string, ...args: any[]): void; 17 | } 18 | 19 | export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' 20 | 21 | export type LoggerType = LoggerMethods & LogLevels; 22 | 23 | export type LogLevels = { 24 | [key in LogLevel]: LogFn; 25 | } 26 | 27 | export interface LoggerMethods { 28 | logger?: any; 29 | child: (options: LoggerConfig) => LoggerType; 30 | for: (fn: Object) => LoggerType; 31 | } 32 | -------------------------------------------------------------------------------- /src/types/meter.ts: -------------------------------------------------------------------------------- 1 | export interface StatsDClientConfig { 2 | prefix?: string; 3 | tcp?: boolean; 4 | socketTimeout?: number; 5 | tags?: { [k: string]: string | number } 6 | } 7 | 8 | export interface StatsDUDPConfig extends StatsDClientConfig { 9 | tcp?: false; 10 | host: string; 11 | port: number; 12 | ipv6?: boolean; 13 | } 14 | 15 | export interface MeterConfig { 16 | statsd?: StatsDUDPConfig 17 | } 18 | 19 | 20 | export interface MetricsCollector { 21 | tick(metric: string, tags?: { [k: string]: string | number }):void; 22 | time(metric: string, duration: number, tags?: { [k: string]: string | number }): void; 23 | } 24 | 25 | 26 | export interface MeterFacade extends MetricsCollector { 27 | timenote(metric: string, tags?: { [k: string]: string | number }): () => number; 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RockME TS 2 | 3 | Library with main components that needs for building Rockstat microservice on Node.js using TypeScript 4 | 5 | ## Usage 6 | 7 | ### Simple components 8 | const log = new Logger(config.log); 9 | const meter = new Meter(config.meter); 10 | 11 | this.log = log.for(this); 12 | this.log.info('Starting service'); 13 | 14 | ### Redis RPC 15 | 16 | // setup Redis 17 | const redisFactory = new RedisFactory({ log, meter, ...config.redis }); 18 | 19 | // Setup RPC 20 | const channels = [config.rpc.name, BROADCAST]; 21 | const rpcOptions: AgnosticRPCOptions = { channels, redisFactory, log, meter, ...config.rpc } 22 | this.rpcAdaptor = new RPCAdapterRedis(rpcOptions); 23 | this.rpc = new RPCAgnostic(rpcOptions); 24 | 25 | this.rpc.setup(this.rpcAdaptor); 26 | this.rpc.register(BROADCAST, this.chw.write); 27 | -------------------------------------------------------------------------------- /src/ids/the-ids.ts: -------------------------------------------------------------------------------- 1 | import { IdGenShowFlake } from './snow_flake'; 2 | import { IdGenRoundCounter } from './round_counter'; 3 | import { hash64 } from 'xxhash'; 4 | import { Uint64BE } from 'int64-buffer' 5 | 6 | 7 | export class TheIds { 8 | 9 | sf: IdGenShowFlake; 10 | rpcCounter: IdGenRoundCounter; 11 | 12 | constructor() { 13 | this.sf = new IdGenShowFlake(); 14 | this.rpcCounter = new IdGenRoundCounter(); 15 | } 16 | 17 | flake(): string { 18 | return this.sf.take(); 19 | } 20 | 21 | round(): string { 22 | return this.rpcCounter.take().toString(36); 23 | } 24 | 25 | xxhash(str: string): string { 26 | const buff = hash64(Buffer.from(str), 0xCACA3ADA, 'buffer') 27 | return new Uint64BE(buff).toString() 28 | } 29 | 30 | static SInt64ToBase64(str: string): string { 31 | return new Uint64BE(str, 10).toBuffer().toString('base64') 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/meter/statsd.ts: -------------------------------------------------------------------------------- 1 | import * as StatsdClient from 'statsd-client' 2 | import { MetricsCollector, StatsDUDPConfig } from '../types'; 3 | 4 | 5 | export class StatsdMeter implements MetricsCollector { 6 | 7 | client: StatsdClient; 8 | 9 | constructor(options: StatsDUDPConfig) { 10 | this.client = new StatsdClient(options); 11 | } 12 | 13 | tick(metric: string, tags?: { [k: string]: string | number }) { 14 | this.client.increment(metric, undefined, tags); 15 | } 16 | 17 | timenote(metric: string, tags?: { [k: string]: string | number }): () => number { 18 | const start = process.hrtime(); 19 | return () => { 20 | const diff = process.hrtime(start); 21 | const time = Math.round(diff[0] * 1e3 + diff[1] * 1e-6); 22 | this.time(metric, time, tags); 23 | return time; 24 | } 25 | } 26 | 27 | time(metric: string, duration: number, tags?: { [k: string]: string | number }): void { 28 | this.client.timing(metric, duration, tags); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "moduleResolution": "node", 5 | "module": "commonjs", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "strictNullChecks": true, 9 | "sourceMap": true, 10 | "pretty": true, 11 | "preserveConstEnums": true, 12 | "removeComments": true, 13 | "allowUnreachableCode": true, 14 | "noFallthroughCasesInSwitch": false, 15 | "allowSyntheticDefaultImports": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "noImplicitAny": true, 18 | "declaration": true, 19 | "declarationMap": false, 20 | "outDir": "./dist/", 21 | "typeRoots": [ 22 | "node_modules/@types", 23 | "@types" 24 | ], 25 | "baseUrl": "./", 26 | "paths": { 27 | "@app/*": ["src/*"] 28 | } 29 | }, 30 | "include": [ 31 | "./src/**/*", 32 | "./node_modules/@types/**/*.ts" 33 | ], 34 | "exclude": [ 35 | "node_modules" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /src/log/pino.ts: -------------------------------------------------------------------------------- 1 | import * as pino from 'pino'; 2 | import { LogLevel, LogFn, LoggerType, LoggerConfig } from '../types'; 3 | 4 | export class Logger implements LoggerType { 5 | 6 | warn: LogFn; 7 | fatal: LogFn; 8 | error: LogFn; 9 | debug: LogFn; 10 | info: LogFn; 11 | trace: LogFn; 12 | 13 | logger: pino.Logger; 14 | 15 | methods: LogLevel[] = ['trace', 'info', 'debug', 'warn', 'error', 'fatal']; 16 | 17 | constructor(config: LoggerConfig, instance?: pino.Logger) { 18 | const pinoConig = config.pino; 19 | this.logger = instance && instance.child(pinoConig) || pino(pinoConig); 20 | for (const method of this.methods) { 21 | this[method as LogLevel] = this.logger[method].bind(this.logger); 22 | } 23 | } 24 | 25 | for(object: object): Logger { 26 | const pinoConfig = { 27 | name: object.constructor.name 28 | }; 29 | return this.child({ pino: pinoConfig }); 30 | } 31 | 32 | child(config: LoggerConfig): Logger { 33 | return new Logger(config, this.logger); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/meter/meter.ts: -------------------------------------------------------------------------------- 1 | import { MeterConfig, MetricsCollector, MeterFacade } from '../types'; 2 | import { StatsdMeter } from './statsd'; 3 | 4 | export class Meter implements MeterFacade { 5 | meters: MetricsCollector[] = []; 6 | 7 | constructor(options: MeterConfig) { 8 | if(options.statsd){ 9 | this.meters.push(new StatsdMeter(options.statsd)); 10 | } 11 | } 12 | 13 | tick(metric: string, tags?: { [k: string]: string | number }) { 14 | for(const m of this.meters){ 15 | m.tick(metric, tags); 16 | } 17 | } 18 | 19 | timenote(metric: string, tags?: { [k: string]: string | number }): () => number { 20 | const start = process.hrtime(); 21 | return () => { 22 | const diff = process.hrtime(start); 23 | const time = Math.round(diff[0] * 1e3 + diff[1] * 1e-6); 24 | this.time(metric, time, tags); 25 | return time; 26 | } 27 | } 28 | 29 | time(metric: string, duration: number, tags?: { [k: string]: string | number }): void { 30 | for(const m of this.meters){ 31 | m.time(metric, duration, tags); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/types/redis.ts: -------------------------------------------------------------------------------- 1 | import { MeterFacade } from "./meter"; 2 | import { LoggerType } from "./log"; 3 | 4 | export type RedisMessageHandler = (msg: Array) => void; 5 | 6 | export interface RedisClientType { 7 | on(event: string, func: (...args: any[]) => void): void; 8 | publish(topic: string, raw: any): void; 9 | subscribe(channel: string, func: Function): void; 10 | get(key: string): Promise 11 | hgetall(key: string): Promise | null> 12 | set(key: string, value: string): Promise 13 | } 14 | 15 | export interface RedisFactoryType { 16 | create(): RedisClientType; 17 | } 18 | 19 | export interface RedusListenerFn { 20 | (...args: Array): void; 21 | } 22 | 23 | export interface RedisConfig { 24 | dsn: string; 25 | } 26 | 27 | // export interface RedisConfig { 28 | // host: string; 29 | // port: number; 30 | // db: number; 31 | // auth: boolean; 32 | // maxRetries: number; 33 | // tryToReconnect: boolean; 34 | // reconnectTimeout: number; 35 | // autoConnect: boolean; 36 | // doNotSetClientName: boolean; 37 | // doNotRunQuitOnEnd: boolean; 38 | // } 39 | 40 | export interface RedisClientOptions extends RedisConfig { 41 | log?: LoggerType; 42 | meter?: MeterFacade; 43 | } 44 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | PERCENT := % 2 | DEL := / 3 | BR != shell git branch | grep \* | cut -d ' ' -f2- 4 | 5 | 6 | bump-patch: 7 | bumpversion patch 8 | 9 | bump-minor: 10 | bumpversion minor 11 | 12 | up_master: 13 | @echo "on branch $(BR)" 14 | 15 | @[ "$(BR)" == "dev" ] && true || (echo "only dev can be used. you on $(BR)" && exit 1) 16 | @[ -z "$(git status --porcelain)" ] && true || (echo "directory not clean. commit changes first" && exit 1) 17 | @git checkout master && git rebase dev && git push origin master && git checkout dev \ 18 | && echo "master rebased and pushed" 19 | 20 | to_master: 21 | @echo $(BR) 22 | git checkout master && git rebase $(BR) && git checkout $(BR) 23 | 24 | build: 25 | docker build -t band-base-ts . 26 | 27 | push-latest: 28 | docker tag band-base-ts rockstat/band-base-ts:latest 29 | docker push rockstat/band-base-ts:latest 30 | 31 | push-dev: 32 | docker tag band-base-ts rockstat/band-base-ts:dev 33 | docker push rockstat/band-base-ts:dev 34 | 35 | 36 | push: 37 | git push origin master 38 | git push origin dev 39 | 40 | travis-trigger: 41 | curl -vv -s -X POST \ 42 | -H "Content-Type: application/json" \ 43 | -H "Accept: application/json" \ 44 | -H "Travis-API-Version: 3" \ 45 | -H "Authorization: token $$TRAVIS_TOKEN" \ 46 | -d '{ "request": { "branch":"$(br)" }}' \ 47 | https://api.travis-ci.com/repo/$(subst $(DEL),$(PERCENT)2F,$(repo))/requests 48 | -------------------------------------------------------------------------------- /@types/xxhash/index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for node-xxhash 2 | // Project: https://github.com/mscdex/node-xxhash 3 | // Definitions by: Dmitry Rodin 4 | // TypeScript Version: 2.7 5 | 6 | /// 7 | 8 | declare module 'xxhash' { 9 | 10 | import { Duplex } from "stream"; 11 | 12 | type EncBuf = 'buffer' | 'hex' | 'base64' | 'binary' | Buffer; 13 | type Bits = 32 | 64; 14 | 15 | class XXHash { 16 | 17 | /** 18 | * 19 | * @param data 20 | * @param seed unsigned integer or a Buffer containing (1 <= n <= 4) b 21 | * @param encbuf string [buffer, hex, base64, or binary] or Buffer of at least 4 bytes. The default value for encbuf is 'buffer'. 22 | */ 23 | static hash(data: Buffer, seed: number | Buffer, encbuf?: EncBuf): number | Buffer; 24 | 25 | /** 26 | * 27 | * @param data 28 | * @param seed be an unsigned integer or a Buffer containing (1 <= n <= 8) 29 | * @param encbuf string [buffer, hex, base64, or binary] or Buffer of at least 8 bytes. The default value for encbuf is 'buffer'. 30 | */ 31 | static hash64(data: Buffer, seed: number | Buffer, encbuf?: EncBuf | Buffer): T; 32 | 33 | static Stream(seed: number | Buffer, bits?: Bits, encbuf?: EncBuf): Duplex; 34 | 35 | constructor(seed: number | Buffer); 36 | 37 | update(data: Buffer): void; 38 | 39 | digest(encbuf: EncBuf): number | Buffer; 40 | 41 | } 42 | 43 | namespace XXHash { } 44 | export = XXHash; 45 | } 46 | -------------------------------------------------------------------------------- /@types/redis-fast-driver/index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for 2 | // Project: 3 | // Definitions by: Dmitry Rodin 4 | // TypeScript Version: 2.7 5 | 6 | 7 | // https://github.com/djanowski/yoredis 8 | 9 | /// 10 | 11 | 12 | declare module 'redis-fast-driver' { 13 | 14 | import { EventEmitter } from "events"; 15 | 16 | 17 | class Redis extends EventEmitter { 18 | constructor(options: RedisOptions); 19 | 20 | connect(): void; 21 | reconnect(): void; 22 | end(): void; 23 | selectDb(cb: Function): void; 24 | sendAuth(cb: Function): void; 25 | rawCall(args: Array, cb: Function): void; 26 | rawCallAsync(args: Array): Promise 27 | } 28 | 29 | interface RedisOptions { 30 | host: string; 31 | port: number; 32 | db: number; 33 | auth?: boolean; 34 | maxRetries?: number; 35 | tryToReconnect?: boolean; 36 | reconnectTimeout?: number; 37 | autoConnect?: boolean; 38 | doNotSetClientName?: boolean; 39 | doNotRunQuitOnEnd?: boolean; 40 | } 41 | 42 | namespace Redis { 43 | interface RedisOptions { 44 | host: string; 45 | port: number; 46 | db: number; 47 | auth?: boolean; 48 | maxRetries?: number; 49 | tryToReconnect?: boolean; 50 | reconnectTimeout?: number; 51 | autoConnect?: boolean; 52 | doNotSetClientName?: boolean; 53 | doNotRunQuitOnEnd?: boolean; 54 | } 55 | } 56 | 57 | // const RootRedis: Redis.Redis & { Redis: new () => Redis.Redis }; 58 | 59 | export = Redis; 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/rpc/adapter/redis.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AgnosticRPCOptions, 3 | RedisClientType, 4 | LoggerType 5 | } from "../../types"; 6 | import * as EventEmitter from 'eventemitter3'; 7 | import { RPCAdapter } from '../../types'; 8 | import { StubLogger } from "../../log"; 9 | 10 | export class RPCAdapterRedis extends EventEmitter implements RPCAdapter { 11 | 12 | options: AgnosticRPCOptions; 13 | started: boolean; 14 | rsub: RedisClientType; 15 | rpub: RedisClientType; 16 | log: LoggerType; 17 | 18 | constructor(options: AgnosticRPCOptions) { 19 | super(); 20 | this.options = options; 21 | const { 22 | redisFactory, 23 | channels, 24 | log 25 | } = options; 26 | 27 | this.log = log ? log.for(this) : new StubLogger(); 28 | this.rsub = redisFactory.create(); 29 | 30 | this.rsub.on('connect', () => { 31 | for (const chan of channels) { 32 | this.rsub.subscribe(chan, this.redisMsg); 33 | } 34 | }) 35 | 36 | this.rpub = redisFactory.create(); 37 | this.log.info('started'); 38 | } 39 | 40 | private decode(raw: string): any { 41 | try { 42 | return JSON.parse(raw); 43 | } catch (error) { 44 | this.log.error('Redis decode payload error', error); 45 | return; 46 | } 47 | } 48 | 49 | private encode(data: any): string | void { 50 | try { 51 | return JSON.stringify(data); 52 | } catch (error) { 53 | this.log.error('Redis encode payload error', error); 54 | return; 55 | } 56 | } 57 | 58 | send(to: string, msg: any): void { 59 | const raw = this.encode(msg); 60 | this.rpub.publish(to, raw); 61 | } 62 | 63 | redisMsg = (redismsg: Array) => { 64 | if (redismsg[0] === 'message' || redismsg[0] === 'pmessage') { 65 | const raw = redismsg[redismsg.length - 1]; 66 | const msg = this.decode(raw); 67 | if (msg && msg.jsonrpc === '2.0') { 68 | this.emit('message', msg); 69 | } 70 | } else { 71 | this.log.debug('unhandled cmd', redismsg); 72 | } 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/types/rpc.ts: -------------------------------------------------------------------------------- 1 | 2 | import { MeterFacade } from "./meter"; 3 | import { RedisFactoryType } from "./redis"; 4 | import { Logger } from "../log"; 5 | 6 | // RPC 7 | 8 | export interface RPCRequestHandler { 9 | (arg: any): void; 10 | } 11 | 12 | export type RequestHandler = (params: T) => any; 13 | 14 | export interface RpcMethods { 15 | [k: string]: RequestHandler; 16 | } 17 | 18 | export interface RPCAdapter { 19 | send(to: string, msg: any): void; 20 | on(event: string, fn: RPCRequestHandler, context?: any): this; 21 | } 22 | 23 | export interface RPCRegisterStruct { 24 | methods?: Array<[string, string, string]> 25 | } 26 | 27 | 28 | export interface RPCRegisterHandler { 29 | (params: RPCRegisterStruct): any 30 | } 31 | 32 | export interface RPCBase { 33 | jsonrpc: '2.0'; 34 | to: string; 35 | from: string; 36 | } 37 | 38 | export type RPCRequestParams = { [k: string]: any } | null; 39 | 40 | export interface RPCRequest extends RPCBase { 41 | id?: string; 42 | method: string; 43 | params: RPCRequestParams; 44 | } 45 | 46 | export interface RPCRequestOptions { 47 | timeout?: number; 48 | services?: string[]; 49 | } 50 | 51 | export interface RPCResponse extends RPCBase { 52 | id: string; 53 | result: any; 54 | cache?: number; 55 | } 56 | 57 | export interface RPCErrorDetails { 58 | message: string; 59 | code: number; 60 | data?: any; 61 | } 62 | 63 | export interface RPCResponseError extends RPCBase { 64 | id: string; 65 | error: RPCErrorDetails; 66 | } 67 | 68 | export interface RPCWaitingCall { 69 | resolve: (value?: any | PromiseLike) => void; 70 | reject: (reason?: any) => void; 71 | timing: Function; 72 | timeout: NodeJS.Timer; 73 | params: RPCRequestParams; 74 | multi: boolean; 75 | bag: { [k: string]: any }; 76 | services: string[]; 77 | } 78 | 79 | export type RPCWaitingCalls = { [k: string]: RPCWaitingCall | undefined }; 80 | 81 | export interface RPCConfig { 82 | name: string; 83 | listen_all?: boolean; 84 | listen_enrich?: boolean; 85 | listen_direct?: boolean; 86 | } 87 | 88 | 89 | export interface AgnosticRPCOptions extends RPCConfig { 90 | redisFactory: RedisFactoryType; 91 | channels: Array; 92 | log?: Logger; 93 | meter?: MeterFacade; 94 | } 95 | 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rockstat/rock-me-ts", 3 | "version": "1.8.5", 4 | "description": "Package contains Agnostic JSON-RPC2 client/server implementation and other Rockstat requirements for microservice development", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "engines": { 8 | "node": ">=8" 9 | }, 10 | "scripts": { 11 | "start": "ts-node-dev -r 'tsconfig-paths/register' ./src/index.ts | pino", 12 | "build": "rimraf dist && tsc -p tsconfig.json", 13 | "prod": "node dist/index", 14 | "clean": "rimraf dist", 15 | "lint": "tslint -c tslint.json -t stylish -p ./tsconfig.json", 16 | "security-check": "nsp check", 17 | "watch": "tsc -w", 18 | "pub": "npm publish --access public", 19 | "patch": "yarn version --loose-semver --new-version patch" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/rockstat/rock-me-ts.git" 24 | }, 25 | "author": "Dmitry Rodin ", 26 | "license": "Apache-2.0", 27 | "bugs": { 28 | "url": "https://github.com/rockstat/rock-me-ts/issues" 29 | }, 30 | "files": [ 31 | ".editorconfig", 32 | ".gitignore", 33 | "LICENSE", 34 | "package-lock.json", 35 | "package.json", 36 | "README.md", 37 | "tsconfig.json", 38 | "yarn.lock", 39 | "@types/**", 40 | "dist/**", 41 | "src/**" 42 | ], 43 | "homepage": "https://rstat.org", 44 | "dependencies": { 45 | "@types/bluebird": "^3.5.20", 46 | "@types/dotenv": "^4.0.3", 47 | "@types/ejs": "^2.5.1", 48 | "@types/glob": "^5.0.35", 49 | "@types/js-yaml": "^3.11.1", 50 | "@types/lru-cache": "^4.1.1", 51 | "@types/node": "9.6.18", 52 | "@types/pino": "^4.16.0", 53 | "@types/statsd-client": "^0.4.0", 54 | "bluebird": "^3.5.1", 55 | "cctz": "^1.7.7", 56 | "dotenv": "^6.0.0", 57 | "ejs": "^2.6.1", 58 | "eventemitter3": "^3.1.0", 59 | "flake-idgen": "^1.1.0", 60 | "glob": "^7.1.2", 61 | "int64-buffer": "^0.1.10", 62 | "js-yaml": "^3.11.0", 63 | "lru-cache": "^4.1.3", 64 | "merge-options": "^1.0.1", 65 | "pino": "^4.16.1", 66 | "redis-fast-driver": "^2.1.2", 67 | "statsd-client": "^0.4.2", 68 | "xxhash": "^0.2.4" 69 | }, 70 | "devDependencies": { 71 | "rimraf": "^2.6.2", 72 | "source-map-support": "^0.5.6", 73 | "ts-node-dev": "^1.0.0-pre.29", 74 | "tsconfig-paths": "^3.3.2", 75 | "typescript": "^2.9.1" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/config/app_config.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | import { safeLoadAll } from 'js-yaml'; 3 | import { render as ejsRender } from 'ejs'; 4 | import { sync as globSync } from 'glob'; 5 | import * as dotenv from 'dotenv'; 6 | import * as mergeOptions from 'merge-options'; 7 | import { 8 | ENV_PROD, 9 | ENV_STAGE, 10 | ENV_DEV 11 | } from './constants'; 12 | import * as consts from './constants'; 13 | import { 14 | ConfigRoot, 15 | AppConfigOptions, 16 | ImplicitConfig, 17 | AbstractConfig, 18 | Envs 19 | } from '../types'; 20 | dotenv.config(); 21 | dotenv.config({ path: '.env.local' }); 22 | 23 | export class AppConfig { 24 | 25 | configDir: string = './config'; 26 | config: AbstractConfig; 27 | env: Envs; 28 | 29 | get identify() { return this.get('identify'); } 30 | get http() { return this.get('http'); } 31 | get ws() { return this.get('websocket'); } 32 | get log() { return this.get('log'); } 33 | get static() { return this.get('static'); } 34 | get redis() { return this.get('redis'); } 35 | get client() { return this.get('client'); } 36 | get meter() { return this.get('metrics'); } 37 | get rpc() { return this.get('rpc'); } 38 | 39 | /** 40 | * Reading all accessible configuration files including custom 41 | */ 42 | constructor(options: AppConfigOptions = {}) { 43 | this.config = this.load(options); 44 | this.env = this.config.env = AppConfig.env; 45 | } 46 | 47 | load(options: AppConfigOptions): AbstractConfig { 48 | const parts = globSync( 49 | `${this.configDir}/**/*.yml`, { nosort: true } 50 | ).map(file => readFileSync(file).toString()); 51 | const tmplData = { 52 | env: process.env, 53 | envName: AppConfig.env, 54 | consts, 55 | ...(options.vars || {}) 56 | } 57 | const yaml = ejsRender(parts.join('\n'), tmplData, { async: false }); 58 | const docs = safeLoadAll(yaml).filter(cfg => cfg !== null && cfg !== undefined) 59 | const { dev, prod, ...common } = mergeOptions({}, ...docs); 60 | return mergeOptions(common, this.isProd() ? prod : dev); 61 | } 62 | 63 | get>(section: K): AbstractConfig[K] { 64 | return this.config[section] 65 | } 66 | 67 | isDev(): boolean { 68 | return this.env === ENV_DEV; 69 | } 70 | 71 | isProd(): boolean { 72 | return this.env === ENV_PROD; 73 | } 74 | 75 | static get env(): Envs { 76 | switch (process.env.NODE_ENV) { 77 | case 'production': 78 | case 'prod': 79 | return ENV_PROD; 80 | case 'stage': 81 | return ENV_STAGE; 82 | default: 83 | return ENV_DEV; 84 | } 85 | } 86 | 87 | } 88 | 89 | -------------------------------------------------------------------------------- /src/runner/runner.ts: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | Meter 4 | } from '../meter'; 5 | import { 6 | RedisFactory, 7 | } from '../redis'; 8 | import { 9 | TheIds, 10 | } from '../ids'; 11 | import { 12 | AppConfig, 13 | } from '../config'; 14 | import { 15 | Logger, 16 | } from '../log'; 17 | import { 18 | RPCAdapterRedis, 19 | RPCAgnostic, 20 | BROADCAST, 21 | METHOD_STATUS, 22 | SERVICE_DIRECTOR, 23 | METHOD_IAMALIVE 24 | } from '../rpc'; 25 | import { 26 | AgnosticRPCOptions, 27 | RPCAdapter, 28 | ConfigRoot 29 | } from '../types'; 30 | import { AppStatus } from '../rpc' 31 | 32 | export class Deps { 33 | log: Logger; 34 | id: TheIds; 35 | config: AppConfig; 36 | meter: Meter; 37 | rpc: RPCAgnostic; 38 | redisFactory: RedisFactory; 39 | constructor(obj: { 40 | config: AppConfig, 41 | log: Logger, 42 | meter: Meter, 43 | ids: TheIds, 44 | rpc: RPCAgnostic 45 | }) { 46 | Object.assign(this, obj); 47 | } 48 | } 49 | 50 | export class AppRunner { 51 | log: Logger; 52 | deps: Deps; 53 | ids: TheIds; 54 | status: AppStatus; 55 | meter: Meter; 56 | config: AppConfig 57 | name: string; 58 | rpcAdaptor: RPCAdapter; 59 | rpc: RPCAgnostic; 60 | appStarted: Date = new Date(); 61 | 62 | constructor() { 63 | this.config = new AppConfig(); 64 | this.status = new AppStatus(); 65 | this.log = new Logger(this.config.log).for(this); 66 | this.meter = new Meter(this.config.meter); 67 | this.ids = new TheIds(); 68 | this.name = this.config.rpc.name; 69 | this.log.info('Starting service'); 70 | } 71 | 72 | /** 73 | * Required remote functuins 74 | */ 75 | async setup() { 76 | 77 | // setup Redis 78 | const redisFactory = this.deps.redisFactory = new RedisFactory({ log: this.log, meter: this.meter, ...this.config.redis }); 79 | 80 | // Setup RPC 81 | const channels: Array = []; 82 | if (this.config.rpc.listen_all) { 83 | channels.push(BROADCAST); 84 | } 85 | 86 | if (this.config.rpc.listen_direct) { 87 | channels.push(this.name); 88 | } 89 | 90 | const rpcOptions: AgnosticRPCOptions = { 91 | channels, 92 | redisFactory, 93 | log: this.log, 94 | meter: this.meter, 95 | ...this.config.rpc 96 | }; 97 | 98 | this.rpc = new RPCAgnostic(rpcOptions); 99 | 100 | const rpcAdaptor = new RPCAdapterRedis(rpcOptions); 101 | this.rpc.setup(rpcAdaptor); 102 | 103 | this.rpc.register(METHOD_STATUS, this.status.get); 104 | const aliver = () => { 105 | this.rpc.notify(SERVICE_DIRECTOR, METHOD_IAMALIVE, { name: this.name }) 106 | }; 107 | 108 | setTimeout(aliver, 500); 109 | setInterval(aliver, 60000); 110 | this.log.info('Runner setup completed') 111 | } 112 | 113 | 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/redis/client.ts: -------------------------------------------------------------------------------- 1 | import * as Redis from 'redis-fast-driver'; 2 | import { RedisConfig, LoggerType, RedisClientOptions, MeterFacade } from '../types'; 3 | import { StubLogger } from '../log'; 4 | import { StubMeter } from '../meter'; 5 | import { parse as urlParse } from 'url'; 6 | 7 | export class RedisClient { 8 | 9 | options: RedisConfig; 10 | log: LoggerType; 11 | meter: MeterFacade; 12 | client: Redis; 13 | started: boolean = false; 14 | 15 | constructor(options: RedisClientOptions) { 16 | 17 | const { log, meter, ...config } = options; 18 | 19 | this.log = log ? log.for(this) : new StubLogger(); 20 | this.meter = meter ? meter : new StubMeter(); 21 | 22 | 23 | const parts = urlParse(config.dsn) 24 | 25 | // if (parts.hostname && parts.port && parts.path) { 26 | const host: string = parts.hostname || 'localhost'; 27 | const port: number = Number(parts.port || 6379) 28 | const db: number = Number((parts.path || '/0x').slice(1)) 29 | 30 | this.log.info(`Starting redis client. DSN: ${config.dsn}`); 31 | this.client = new Redis({ host, port, db }); 32 | 33 | //happen only once 34 | this.client.on('ready', () => { 35 | this.log.info('redis ready'); 36 | }); 37 | 38 | //happen each time when reconnected 39 | this.client.on('connect', () => { 40 | this.log.info('redis connected'); 41 | }); 42 | 43 | this.client.on('disconnect', () => { 44 | this.log.info('redis disconnected'); 45 | }); 46 | 47 | this.client.on('reconnecting', (num: number) => { 48 | this.log.info('redis reconnecting with attempt #' + num); 49 | }); 50 | 51 | this.client.on('error', (e: Error) => { 52 | this.log.info('redis error', e); 53 | }); 54 | 55 | // called on an explicit end, or exhausted reconnections 56 | this.client.on('end', () => { 57 | this.log.info('redis closed'); 58 | }); 59 | } 60 | 61 | get(key: string): Promise { 62 | return this.client.rawCallAsync(['GET', key]); 63 | } 64 | 65 | hgetall(key: string): Promise> { 66 | return this.client.rawCallAsync(['HGETALL', key]); 67 | } 68 | 69 | set(key: string, value: string): Promise { 70 | return this.client.rawCallAsync(['SET', key, value]); 71 | } 72 | 73 | on = (event: string, func: (...args: any[]) => void) => { 74 | this.client.on(event, func); 75 | } 76 | 77 | publish(topic: string, raw: any): void { 78 | this.client.rawCall(['PUBLISH', topic, raw], (error: Error, msg: any) => { 79 | if (error) { 80 | this.log.error('Redis publish error', error); 81 | } 82 | }) 83 | } 84 | 85 | subscribe(channel: string, func: Function): void { 86 | this.client.rawCall(['SUBSCRIBE', channel], (error: Error, msg: Array) => { 87 | if (error) { 88 | this.log.error('Redis error', error); 89 | return; 90 | } 91 | func(msg); 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | branches: 4 | only: 5 | - master 6 | - dev 7 | env: 8 | global: 9 | - PROJECT_NAME=band-base-ts 10 | - ORG=rockstat 11 | - IMAGE_NAME=$ORG/$PROJECT_NAME 12 | - TAG="$( [ $TRAVIS_BRANCH == 'master' ] && echo latest || echo $TRAVIS_BRANCH )" 13 | - secure: kkIK2IbBnsVvdQyJiuqctZrH178qRVUkPp5/R7/18FOCru87U3puVd//pLH3uFSaQGm461AJ+ur7Cd6iO3mPbkk8TK6X5P9i6fV15cSRQt5vOl8Y51ymBkfNH6l+UNFe8LJiPpfDZ0IVCPkBIEhJccXBSt7bliwIJofvYxgxCzsr9IJWQaWZxN6jzo6OSLZgPlpqTzmhF9OgCTLhh5/NyZPlh3mZHnm4RNItZhFjs0NuTHBbI5PY9zbqluxqEvd/Q1PrjfYOdHMoO0KtcJnlrie4xBoVuRs/JuwoToF+QZH8pknSC6hs7eeqBWOZNw5bpnCOmoKRvOte17U8EBaCEuTYl+s33aL9X7C8sfIUw8QvXQ7ao7LwpEs6YSV5+w3BZLIDUusCMdUVKzyoF4YGF1bfaUNRoP5afjkiPv+sKl9IDJkfkPw1sAXlcdL9VMngUcvDy1qdi7L8RX7Z7xqv/fmGH8eKomkJDwaXL1xOEcCrSwUcsrpfzVy6EjvUJyuYauwFD5auvyZprSsSfsgre0Q5i5aCvz4f//7+Fc3GlWkCMw5Z2HeBxTCWq+h1cQ9Ct/GhGvNk4w/yQ31/dRjPfG96RpYIl6bYgdDgJU8/3b494/Tcy9wiObeqZ/DeNkegs14LGd4O5D7brYQ+GxFdZyqrR0sWLsaX+cNlelaLZEE= 14 | - secure: qKYPKAUo1vOwMUhlPnreMcJHTmvhEDob9OuVZZJKCLoSCxUUHotQ5GXY8eFzHM/aRfOVw0KlPRMcPZOY3bkAA4AQjstE/im+Fs04+9xdI6n+SMilQhP6bIuM6oVpIlrBynsnG5at49T1MQhYpfoXnT7KnxP+O6OetwW3Oe4o+DOPV0KiZmtC7OP+VxL55LDFVYjWLmJWTUnVHQfWJgRscyrJe8Yjwpd+HcAOz8ZmkPfImtSWyymsszyP99zklYPTaCNM9094p6dr9/0xNbyW/KaT3khH7yP/dwlJdbAS/4JB2GTqNb8QcoEj7x+DK6IJh+/OC04FCBRuMMdNDWnAf1+VCUxMyq29yxhq66ZqTXjO2odC+ds9+TXqunag9hNjShgLulOtjCvzAN6EnC5A5lkrVG9AAVUXndu4BiehG3HZMZz0GCOM8qkVaYeMcf3HwPAQllrRKpm+NQn15yc0XUkoZwyECnMayDTDF8/PN0RGn1y7Nj2U5ZWsC7Xw4Is5ZJ7jL3xE21oxyP+ww2RPoMjKxtTbEdA1ldUul3ZrbxLXNl7BOmvVOG0b7pU1Hya3nQBX0SMirvgUHpVhgX4kZnGeehO2GL3peVyjCjZ7db8+FJqzb6s877D77r11ZAgSNo6citOM79JJdk02MMIrJBY4EmYMBEf08NnGX3lCg8o= 15 | - secure: TLf74VRbUEYhX7bbmGKw2nV0gVtkrHKwBvjdbeDuUG3YjlOvrlhxYkiJMU8eP7hUktd6mbt4lzjX01ydbtbGDQvjfG6NJFt80UPdTv7BP97hT4F6JI58HMACgNOFKBcWvLrY3njF+Wg34aXETb+hle81zHKj92rLwLDHD33IIV2dRG9yhbOy2ZqS3ldE7rZTF9LUTc4tcGcJ/uPsnTF+cfawNHHJ6MjuTq86EsILbq8+wPXhe2NG1b/56o9TFhIW941Q5dLAIz/O/35ipczlwaATlVRdPqn4kYv1PzuukGBEjc0vmnJukYvrOIX/jmFcVhIPynX20rD/XcwV0xC/ddnOe2Uu6QHbigmugugvMbZYvLGw3QPiQBrvd12kHO9XU3UQvn4B30EtbuGRf9LoqtYOI0nhE6rJfwRfjPD4XyENRdQIcYt+98bYJUEhsp5YETPzwhafB2t3aoi+0wmb23Z2XcgbP0hkJvefluEDB6soRhzdh3fepTWMcTMJDm1w1eKnhf3OFDdfryDpcrGocH9j8w5TMMQIoRNlWzBz3SKCJaDLS2lDMwhnzEP4TooR/M/ZtMIN+EGzysRCR1JZ7fqjjPco/HCsuMrz+HxnnO1L4MBPBQMw2d24ugT9TQEcdWZEItsN7O53YKZQXPNDLphuygMFmkX2uJ/qNewbIWQ= 16 | services: 17 | - docker 18 | script: 19 | - cat Dockerfile | sed "s/\:latest/\:$TAG/g" > Dockerfile.tag 20 | - docker build -t $PROJECT_NAME -f Dockerfile.tag . 21 | after_script: 22 | - docker images 23 | before_deploy: 24 | - docker tag "$PROJECT_NAME" "$IMAGE_NAME:$TAG" 25 | - docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD" 26 | after_deploy: 27 | - make repo=rockstat/front br=$TRAVIS_BRANCH travis-trigger 28 | - make repo=rockstat/chwriter br=$TRAVIS_BRANCH travis-trigger 29 | deploy: 30 | provider: script 31 | skip_cleanup: true 32 | script: docker push "$IMAGE_NAME:$TAG" 33 | on: 34 | all_branches: true 35 | -------------------------------------------------------------------------------- /src/structs/response.ts: -------------------------------------------------------------------------------- 1 | export const STATUS_OK = 200; 2 | export const STATUS_OK_NO_CONTENT = 204; 3 | export const STATUS_BAD_REQUEST = 400; 4 | export const STATUS_NOT_FOUND = 404; 5 | export const STATUS_TEAPOT = 418; 6 | export const STATUS_INT_ERROR = 500; 7 | export const STATUS_TEMP_REDIR = 302; 8 | export const STATUS_PERSIST_REDIR = 301; 9 | export const STATUS_SEE_OTHERS_REDIR = 303; 10 | export const STATUS_UNKNOWN = 'Unknown Status'; 11 | 12 | 13 | export const STATUS_DESCRIPTIONS = { 14 | [STATUS_OK]: 'Ok', 15 | [STATUS_OK_NO_CONTENT]: 'No Content', 16 | [STATUS_BAD_REQUEST]: 'Bad Request', 17 | [STATUS_NOT_FOUND]: 'Not Found', 18 | [STATUS_TEAPOT]: "I'm a teapot", 19 | [STATUS_INT_ERROR]: 'Internal Error', 20 | [STATUS_TEMP_REDIR]: 'Temporary Redirect', 21 | [STATUS_PERSIST_REDIR]: 'Temporary Redirect', 22 | [STATUS_SEE_OTHERS_REDIR]: 'See Others' 23 | } 24 | export type HTTPCodes = keyof typeof STATUS_DESCRIPTIONS; 25 | 26 | // === Header 27 | 28 | export type HTTPHeader = [string, number | string | string[]]; 29 | export type HTTPHeaders = Array; 30 | 31 | const RESP_TYPE_KEY = 'type__'; 32 | const RESP_PIXEL = 'pixel' 33 | const RESP_REDIRECT = 'redirect' 34 | const RESP_ERROR = 'error' 35 | const RESP_DATA = 'data' 36 | 37 | export { 38 | RESP_TYPE_KEY, 39 | RESP_PIXEL, 40 | RESP_REDIRECT, 41 | RESP_ERROR, 42 | RESP_DATA 43 | } 44 | 45 | export type ResponseTypeRedirect = typeof RESP_REDIRECT; 46 | export type ResponseTypePixel = typeof RESP_PIXEL; 47 | export type ResponseTypeError = typeof RESP_ERROR; 48 | export type ResponseTypeData = typeof RESP_DATA; 49 | 50 | export type BandResponseDataType = { [k: string]: any } | Array | string | number | null; 51 | 52 | export interface BandResponseBase { 53 | type__: ResponseTypeRedirect | ResponseTypePixel | ResponseTypeError | ResponseTypeData; 54 | id__?: string; 55 | native__?: boolean; 56 | headers: HTTPHeaders; 57 | statusCode: number; 58 | } 59 | 60 | export type BandResponseRedirect = { 61 | type__: ResponseTypeRedirect; 62 | location: string; 63 | } & BandResponseBase; 64 | 65 | export type BandResponsePixel = { 66 | type__: ResponseTypePixel; 67 | } & BandResponseBase; 68 | 69 | export type BandResponseError = { 70 | type__: ResponseTypeError; 71 | errorMessage: string; 72 | } & BandResponseBase; 73 | 74 | export type BandResponseData = { 75 | type__: ResponseTypeData; 76 | contentType?: string; 77 | data: BandResponseDataType; 78 | } & BandResponseBase; 79 | 80 | // export type ResponseTypes = ResponseTypeRedirect | ResponseTypeError | ResponseTypePixel | ResponseTypeData | ResponseTypeEmpty; 81 | // export interface BandResponseFull { 82 | // location?: string; 83 | // statusCode?: number; 84 | // type: ResponseTypes; 85 | // errorMessage?: string; 86 | // data?: { [k: string]: any } | Buffer | string | number | null; 87 | // } 88 | 89 | export type BandResponse = BandResponseData 90 | | BandResponsePixel 91 | | BandResponseRedirect 92 | | BandResponseError 93 | 94 | 95 | export type UnknownResponse = BandResponse | string | number | Array | Object; 96 | 97 | export type BandRedirectResponseBuilder = (params: { location: string, statusCode?: HTTPCodes, headers?: HTTPHeaders }) => BandResponseRedirect; 98 | 99 | const redirect: BandRedirectResponseBuilder = ({ location, statusCode, headers }) => { 100 | return { 101 | type__: RESP_REDIRECT, 102 | headers: headers || [], 103 | location, 104 | statusCode: statusCode || STATUS_TEMP_REDIR, 105 | } 106 | } 107 | 108 | export type BandPixelResponseBuilder = (params: { statusCode?: HTTPCodes, headers?: HTTPHeaders }) => BandResponsePixel; 109 | 110 | const pixel: BandPixelResponseBuilder = ({ statusCode, headers }) => { 111 | return { 112 | type__: RESP_PIXEL, 113 | headers: headers || [], 114 | statusCode: statusCode || STATUS_OK, 115 | type: RESP_PIXEL 116 | } 117 | } 118 | 119 | export type BandErrorResponseBuilder = (params: { statusCode?: HTTPCodes, errorMessage?: string, headers?: HTTPHeaders }) => BandResponseError; 120 | 121 | const error: BandErrorResponseBuilder = ({ statusCode, errorMessage, headers }) => { 122 | statusCode = statusCode || STATUS_INT_ERROR; 123 | return { 124 | type__: RESP_ERROR, 125 | headers: headers || [], 126 | statusCode: statusCode, 127 | errorMessage: errorMessage || STATUS_DESCRIPTIONS[statusCode], 128 | } 129 | } 130 | 131 | export type BandDataResponsdeBuilder = (params: { data?: BandResponseData['data'], statusCode?: HTTPCodes, headers?: HTTPHeaders, contentType?: string }) => BandResponseData; 132 | 133 | const data: BandDataResponsdeBuilder = ({ data, statusCode, headers, contentType }) => { 134 | return { 135 | type__: RESP_DATA, 136 | headers: headers || [], 137 | statusCode: statusCode || STATUS_OK, 138 | contentType: contentType, 139 | data: data || {} 140 | } 141 | } 142 | 143 | /** 144 | * Check is correct band response 145 | * @param msg 146 | */ 147 | export const isBandResponse = (msg: UnknownResponse) => { 148 | return msg !== undefined 149 | && msg !== null 150 | && typeof msg === 'object' 151 | && !Array.isArray(msg) 152 | && RESP_TYPE_KEY in msg; 153 | } 154 | 155 | export const response = { 156 | redirect, 157 | pixel, 158 | error, 159 | data 160 | } 161 | -------------------------------------------------------------------------------- /src/rpc/agnostic.ts: -------------------------------------------------------------------------------- 1 | import { reject, method } from "bluebird"; 2 | import { TheIds } from "../ids"; 3 | import { RPCAdapter, RPCWaitingCall, RPCRequestOptions } from "../types" 4 | import { StubLogger } from '../log' 5 | import { StubMeter } from '../meter' 6 | 7 | import { 8 | AgnosticRPCOptions, 9 | RPCWaitingCalls, 10 | RpcMethods, 11 | LoggerType, 12 | RPCRequest, 13 | RPCResponse, 14 | RPCResponseError, 15 | RPCRequestParams, 16 | MeterFacade, 17 | RequestHandler 18 | } from "../types"; 19 | 20 | const RPC20 = '2.0'; 21 | 22 | export class RPCAgnostic { 23 | 24 | ids: TheIds; 25 | meter: MeterFacade; 26 | started: boolean = false; 27 | timeout: number; 28 | log: LoggerType; 29 | queue: RPCWaitingCalls = {}; 30 | methods: RpcMethods = {}; 31 | adapter: RPCAdapter; 32 | listen_direct: boolean; 33 | listen_all: boolean; 34 | name: string; 35 | 36 | constructor(options: AgnosticRPCOptions) { 37 | const { name, listen_all, listen_direct, log, meter } = options; 38 | this.ids = new TheIds(); 39 | this.name = name; 40 | this.timeout = 200; 41 | this.listen_all = listen_all || false; 42 | this.listen_direct = listen_direct || true; 43 | this.log = log ? log : new StubLogger(); 44 | this.meter = meter ? meter : new StubMeter(); 45 | } 46 | 47 | setup(adapter: RPCAdapter) { 48 | this.adapter = adapter; 49 | this.adapter.on('message', (msg) => { 50 | this.dispatch(msg) 51 | }) 52 | this.log.info('started'); 53 | } 54 | 55 | 56 | /** 57 | * Resolve waiting waiter 58 | * @param id 59 | * @param result 60 | * @param call 61 | */ 62 | resolve(id: string, result: any, call: RPCWaitingCall) { 63 | this.log.debug('resolve') 64 | if (call.resolve && result !== undefined) { 65 | call.timing(); 66 | call.resolve(result); 67 | this.cleanWaiter(id, call) 68 | } else { 69 | call.reject(new Error('Bad params to resolve request')) 70 | } 71 | } 72 | 73 | /** 74 | * Cleaning request state 75 | * @param id RPC request ID 76 | * @param call waiter state struct 77 | */ 78 | cleanWaiter(id: string, call?: RPCWaitingCall) { 79 | if (call && call.timeout) { 80 | clearTimeout(call.timeout) 81 | } 82 | this.queue[id] = undefined; 83 | } 84 | 85 | onTimeout = (id: string) => { 86 | const call = this.queue[id]; 87 | if (call) { 88 | this.queue[id] = undefined; 89 | if (call.multi) { 90 | call.resolve(call.bag); 91 | } 92 | else { 93 | call.reject(new Error('Request timeout')); 94 | } 95 | this.cleanWaiter(id); 96 | } else { 97 | this.log.error(`onTimeout called but call ${id} not found in stack`); 98 | } 99 | } 100 | 101 | async dispatch(msg: RPCResponse | RPCResponseError | RPCRequest): Promise { 102 | // preparing incoming data 103 | if ('method' in msg && msg.method !== undefined) { 104 | const names = msg.method.split(':'); 105 | if (names.length === 3) { 106 | msg.to = names[0]; 107 | msg.method = names[1]; 108 | msg.from = names[2]; 109 | } 110 | } 111 | // Handling request 112 | if ('method' in msg && msg.method !== undefined && msg.to && msg.params !== undefined && msg.method in this.methods) { 113 | this.dispatchRequest(msg).then(res => { 114 | if (res !== undefined) { 115 | this.publish(res); 116 | } 117 | // response can be empty if case of is notification 118 | }) 119 | return; 120 | } 121 | // Handling response 122 | else if ('id' in msg && msg.id !== undefined && ('result' in msg || 'error' in msg)) { 123 | this.dispatchResponse(msg) 124 | return; 125 | } 126 | 127 | this.log.warn('unhandled message at dispatch', msg); 128 | } 129 | 130 | 131 | async dispatchRequest(msg: RPCRequest): Promise { 132 | const { method, from } = msg; 133 | try { 134 | const result = await this.methods[method](msg.params || {}); 135 | if ('id' in msg && msg.id !== undefined) { 136 | return { 137 | jsonrpc: RPC20, 138 | id: msg.id, 139 | from: this.name, 140 | to: from, 141 | result: result || null 142 | } 143 | } 144 | } catch (error) { 145 | return this.wrapError(msg, error); 146 | this.log.error('handler exec error', error); 147 | } 148 | } 149 | 150 | 151 | async dispatchResponse(msg: RPCResponse | RPCResponseError): Promise { 152 | const call = this.queue[msg.id]; 153 | if (call) { 154 | // handling multi-destination request 155 | if (call.multi) { 156 | const idx = call.services.indexOf(msg.from); 157 | if (idx >= 0) { 158 | call.services.splice(idx, 1) 159 | if ('result' in msg) { 160 | call.bag[msg.from] = msg.result; 161 | // complete 162 | if (call.services.length === 0 && call.resolve) { 163 | this.resolve(msg.id, call.bag, call); 164 | this.cleanWaiter(msg.id, call) 165 | } 166 | } else if ('error' in msg) { 167 | this.log.warn(`Received error response from ${msg.from}`, msg); 168 | } else { 169 | this.log.warn(`Unknown message struce`, msg); 170 | } 171 | } else { 172 | // unneeded responses 173 | } 174 | return; 175 | } 176 | // single requests 177 | else { 178 | if ('result' in msg && call.resolve) { 179 | this.resolve(msg.id, msg.result, call); 180 | return; 181 | } 182 | else if ('error' in msg && call.reject) { 183 | this.meter.tick('rpc.error') 184 | this.log.warn('rpc error') 185 | call.reject(msg.error); 186 | return; 187 | } 188 | } 189 | } 190 | this.log.warn('unhandled message at dispatchResponse', { msg, call }); 191 | } 192 | 193 | /** 194 | * Low-level function for send messages 195 | * @param msg RPC-compatible message 196 | */ 197 | publish(msg: RPCRequest | RPCResponse | RPCResponseError): void { 198 | if ('method' in msg && msg.method !== undefined) { 199 | msg.method = `${msg.to}:${msg.method}:${this.name}`; 200 | } 201 | this.adapter.send(msg.to, msg) 202 | } 203 | 204 | /** 205 | * Send notification to remote service(s) 206 | * @param target target service or channel 207 | * @param method remote method to call 208 | * @param params remote method params 209 | */ 210 | notify(target: string, method: string, params: RPCRequestParams = null): void { 211 | const msg: RPCRequest = { 212 | jsonrpc: RPC20, 213 | from: this.name, 214 | to: target, 215 | method: method, 216 | params: params 217 | } 218 | this.publish(msg) 219 | } 220 | 221 | /** 222 | * Execute remote function(s) 223 | * @param target target service or channel 224 | * @param method remote method to call 225 | * @param params remote method params 226 | * @param services services list for answer waiting 227 | */ 228 | request(target: string, method: string, params: RPCRequestParams = null, options: RPCRequestOptions = {}): Promise { 229 | return new Promise((resolve, reject) => { 230 | const id = this.ids.round(); 231 | // if destination services is empty do not send request 232 | if (options.services && options.services.length == 0) { 233 | return resolve(); 234 | } 235 | // fill services for simple request 236 | options.services = options.services || []; 237 | const multi = options.services.length > 0; 238 | const msg: RPCRequest = { 239 | jsonrpc: RPC20, 240 | from: this.name, 241 | to: target, 242 | id: id, 243 | method: method, 244 | params: params || null 245 | } 246 | this.queue[id] = { 247 | resolve, 248 | reject, 249 | bag: {}, 250 | multi: multi, 251 | services: options.services, 252 | timing: this.meter.timenote('rpc.request', { target, method }), 253 | params: params, 254 | timeout: setTimeout(() => this.onTimeout(id), options.timeout || this.timeout) 255 | }; 256 | this.publish(msg) 257 | }) 258 | } 259 | 260 | register(method: string, func: RequestHandler): void { 261 | this.methods[method] = func; 262 | } 263 | 264 | wrapError(msg: RPCRequest, error: Error, code?: number): RPCResponseError | undefined { 265 | if ('id' in msg && msg.id !== undefined) { 266 | return { 267 | id: msg.id, 268 | from: this.name, 269 | to: msg.from, 270 | jsonrpc: RPC20, 271 | error: { 272 | code: code || 0, 273 | message: error.message, 274 | data: error.stack || {} 275 | } 276 | } 277 | } 278 | } 279 | } 280 | 281 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Dmitry Rodin 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/bluebird@^3.5.20": 6 | version "3.5.22" 7 | resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.22.tgz#519b87fe3c9d290ca6c06381ffc3040770ab452b" 8 | 9 | "@types/body-parser@*": 10 | version "1.17.0" 11 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" 12 | dependencies: 13 | "@types/connect" "*" 14 | "@types/node" "*" 15 | 16 | "@types/connect@*": 17 | version "3.4.32" 18 | resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" 19 | dependencies: 20 | "@types/node" "*" 21 | 22 | "@types/dotenv@^4.0.3": 23 | version "4.0.3" 24 | resolved "https://registry.yarnpkg.com/@types/dotenv/-/dotenv-4.0.3.tgz#ebcfc40da7bc0728b705945b7db48485ec5b4b67" 25 | dependencies: 26 | "@types/node" "*" 27 | 28 | "@types/ejs@^2.5.1": 29 | version "2.6.0" 30 | resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-2.6.0.tgz#56502bca6e2e1b4cf9351918ca76cdaa93fe3b6c" 31 | 32 | "@types/events@*": 33 | version "1.2.0" 34 | resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" 35 | 36 | "@types/express-serve-static-core@*": 37 | version "4.16.0" 38 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz#fdfe777594ddc1fe8eb8eccce52e261b496e43e7" 39 | dependencies: 40 | "@types/events" "*" 41 | "@types/node" "*" 42 | "@types/range-parser" "*" 43 | 44 | "@types/express@*": 45 | version "4.16.0" 46 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.0.tgz#6d8bc42ccaa6f35cf29a2b7c3333cb47b5a32a19" 47 | dependencies: 48 | "@types/body-parser" "*" 49 | "@types/express-serve-static-core" "*" 50 | "@types/serve-static" "*" 51 | 52 | "@types/glob@^5.0.35": 53 | version "5.0.35" 54 | resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a" 55 | dependencies: 56 | "@types/events" "*" 57 | "@types/minimatch" "*" 58 | "@types/node" "*" 59 | 60 | "@types/js-yaml@^3.11.1": 61 | version "3.11.2" 62 | resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.11.2.tgz#699ad86054cc20043c30d66a6fcde30bbf5d3d5e" 63 | 64 | "@types/lru-cache@^4.1.1": 65 | version "4.1.1" 66 | resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-4.1.1.tgz#b2d87a5e3df8d4b18ca426c5105cd701c2306d40" 67 | 68 | "@types/mime@*": 69 | version "2.0.0" 70 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b" 71 | 72 | "@types/minimatch@*": 73 | version "3.0.3" 74 | resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" 75 | 76 | "@types/node@*": 77 | version "10.5.2" 78 | resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.2.tgz#f19f05314d5421fe37e74153254201a7bf00a707" 79 | 80 | "@types/node@9.6.18": 81 | version "9.6.18" 82 | resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.18.tgz#092e13ef64c47e986802c9c45a61c1454813b31d" 83 | 84 | "@types/pino@^4.16.0": 85 | version "4.16.0" 86 | resolved "https://registry.yarnpkg.com/@types/pino/-/pino-4.16.0.tgz#802d0e8d36009a39c5c9163634a3344073f04be5" 87 | dependencies: 88 | "@types/events" "*" 89 | "@types/node" "*" 90 | 91 | "@types/range-parser@*": 92 | version "1.2.2" 93 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.2.tgz#fa8e1ad1d474688a757140c91de6dace6f4abc8d" 94 | 95 | "@types/serve-static@*": 96 | version "1.13.2" 97 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48" 98 | dependencies: 99 | "@types/express-serve-static-core" "*" 100 | "@types/mime" "*" 101 | 102 | "@types/statsd-client@^0.4.0": 103 | version "0.4.0" 104 | resolved "https://registry.yarnpkg.com/@types/statsd-client/-/statsd-client-0.4.0.tgz#eb17ad9f96cf5669b52c1c925d487217159da295" 105 | dependencies: 106 | "@types/express" "*" 107 | 108 | "@types/strip-bom@^3.0.0": 109 | version "3.0.0" 110 | resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" 111 | 112 | "@types/strip-json-comments@0.0.30": 113 | version "0.0.30" 114 | resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" 115 | 116 | ansi-styles@^3.2.1: 117 | version "3.2.1" 118 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 119 | dependencies: 120 | color-convert "^1.9.0" 121 | 122 | ansicolors@~0.3.2: 123 | version "0.3.2" 124 | resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" 125 | 126 | argparse@^1.0.7: 127 | version "1.0.10" 128 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 129 | dependencies: 130 | sprintf-js "~1.0.2" 131 | 132 | array-find-index@^1.0.1: 133 | version "1.0.2" 134 | resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" 135 | 136 | arrify@^1.0.0: 137 | version "1.0.1" 138 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 139 | 140 | balanced-match@^1.0.0: 141 | version "1.0.0" 142 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 143 | 144 | bindings@^1.2.1: 145 | version "1.3.0" 146 | resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" 147 | 148 | bluebird@^3.5.1: 149 | version "3.5.1" 150 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" 151 | 152 | brace-expansion@^1.1.7: 153 | version "1.1.11" 154 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 155 | dependencies: 156 | balanced-match "^1.0.0" 157 | concat-map "0.0.1" 158 | 159 | buffer-from@^1.0.0: 160 | version "1.1.0" 161 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" 162 | 163 | buffer-from@^1.1.0: 164 | version "1.1.1" 165 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 166 | 167 | builtin-modules@^1.0.0: 168 | version "1.1.1" 169 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 170 | 171 | camelcase-keys@^2.0.0: 172 | version "2.1.0" 173 | resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" 174 | dependencies: 175 | camelcase "^2.0.0" 176 | map-obj "^1.0.0" 177 | 178 | camelcase@^2.0.0: 179 | version "2.1.1" 180 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" 181 | 182 | cardinal@^2.1.1: 183 | version "2.1.1" 184 | resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" 185 | dependencies: 186 | ansicolors "~0.3.2" 187 | redeyed "~2.1.0" 188 | 189 | cctz@^1.7.7: 190 | version "1.7.7" 191 | resolved "https://registry.yarnpkg.com/cctz/-/cctz-1.7.7.tgz#356b2247beeda0246e411bca7e281e6c89b652c9" 192 | dependencies: 193 | bindings "^1.2.1" 194 | nan "^2.4.0" 195 | 196 | chalk@^2.4.1: 197 | version "2.4.1" 198 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 199 | dependencies: 200 | ansi-styles "^3.2.1" 201 | escape-string-regexp "^1.0.5" 202 | supports-color "^5.3.0" 203 | 204 | cli-table@^0.3.1: 205 | version "0.3.1" 206 | resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" 207 | dependencies: 208 | colors "1.0.3" 209 | 210 | cli-usage@^0.1.1: 211 | version "0.1.8" 212 | resolved "https://registry.yarnpkg.com/cli-usage/-/cli-usage-0.1.8.tgz#16479361f3a895a81062d02d9634827c713aaaf8" 213 | dependencies: 214 | marked "^0.5.0" 215 | marked-terminal "^3.0.0" 216 | 217 | color-convert@^1.9.0: 218 | version "1.9.2" 219 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" 220 | dependencies: 221 | color-name "1.1.1" 222 | 223 | color-name@1.1.1: 224 | version "1.1.1" 225 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" 226 | 227 | colors@1.0.3: 228 | version "1.0.3" 229 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" 230 | 231 | concat-map@0.0.1: 232 | version "0.0.1" 233 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 234 | 235 | core-util-is@~1.0.0: 236 | version "1.0.2" 237 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 238 | 239 | currently-unhandled@^0.4.1: 240 | version "0.4.1" 241 | resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" 242 | dependencies: 243 | array-find-index "^1.0.1" 244 | 245 | dateformat@~1.0.4-1.2.3: 246 | version "1.0.12" 247 | resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" 248 | dependencies: 249 | get-stdin "^4.0.1" 250 | meow "^3.3.0" 251 | 252 | debounce@^1.0.0: 253 | version "1.2.0" 254 | resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" 255 | 256 | decamelize@^1.1.2: 257 | version "1.2.0" 258 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 259 | 260 | deepmerge@^2.0.1: 261 | version "2.1.1" 262 | resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.1.tgz#e862b4e45ea0555072bf51e7fd0d9845170ae768" 263 | 264 | diff@^3.1.0: 265 | version "3.5.0" 266 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" 267 | 268 | dotenv@^6.0.0: 269 | version "6.0.0" 270 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.0.0.tgz#24e37c041741c5f4b25324958ebbc34bca965935" 271 | 272 | dynamic-dedupe@^0.2.0: 273 | version "0.2.0" 274 | resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.2.0.tgz#50f7c28684831ecf1c170aab67a1d5311cdd76ce" 275 | dependencies: 276 | xtend "~2.0.6" 277 | 278 | ejs@^2.6.1: 279 | version "2.6.1" 280 | resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" 281 | 282 | end-of-stream@^1.1.0: 283 | version "1.4.1" 284 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" 285 | dependencies: 286 | once "^1.4.0" 287 | 288 | error-ex@^1.2.0: 289 | version "1.3.2" 290 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" 291 | dependencies: 292 | is-arrayish "^0.2.1" 293 | 294 | escape-string-regexp@^1.0.5: 295 | version "1.0.5" 296 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 297 | 298 | esprima@^4.0.0, esprima@~4.0.0: 299 | version "4.0.1" 300 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 301 | 302 | eventemitter3@^3.1.0: 303 | version "3.1.0" 304 | resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" 305 | 306 | fast-json-parse@^1.0.3: 307 | version "1.0.3" 308 | resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" 309 | 310 | fast-safe-stringify@^1.0.8, fast-safe-stringify@^1.2.3: 311 | version "1.2.3" 312 | resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-1.2.3.tgz#9fe22c37fb2f7f86f06b8f004377dbf8f1ee7bc1" 313 | 314 | filewatcher@~3.0.0: 315 | version "3.0.1" 316 | resolved "https://registry.yarnpkg.com/filewatcher/-/filewatcher-3.0.1.tgz#f4a1957355ddaf443ccd78a895f3d55e23c8a034" 317 | dependencies: 318 | debounce "^1.0.0" 319 | 320 | find-up@^1.0.0: 321 | version "1.1.2" 322 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" 323 | dependencies: 324 | path-exists "^2.0.0" 325 | pinkie-promise "^2.0.0" 326 | 327 | flake-idgen@^1.1.0: 328 | version "1.1.0" 329 | resolved "https://registry.yarnpkg.com/flake-idgen/-/flake-idgen-1.1.0.tgz#282c3ab567dfb486ed8be7ba62b12117661e843d" 330 | 331 | flatstr@^1.0.5: 332 | version "1.0.8" 333 | resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.8.tgz#0e849229751f2b9f6a0919f8e81e1229e84ba901" 334 | 335 | foreach@~2.0.1: 336 | version "2.0.5" 337 | resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" 338 | 339 | fs.realpath@^1.0.0: 340 | version "1.0.0" 341 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 342 | 343 | get-stdin@^4.0.1: 344 | version "4.0.1" 345 | resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" 346 | 347 | glob@^7.0.5, glob@^7.1.2: 348 | version "7.1.2" 349 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 350 | dependencies: 351 | fs.realpath "^1.0.0" 352 | inflight "^1.0.4" 353 | inherits "2" 354 | minimatch "^3.0.4" 355 | once "^1.3.0" 356 | path-is-absolute "^1.0.0" 357 | 358 | graceful-fs@^4.1.2: 359 | version "4.1.11" 360 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 361 | 362 | growly@^1.2.0: 363 | version "1.3.0" 364 | resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" 365 | 366 | has-flag@^3.0.0: 367 | version "3.0.0" 368 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 369 | 370 | hosted-git-info@^2.1.4: 371 | version "2.7.1" 372 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" 373 | 374 | indent-string@^2.1.0: 375 | version "2.1.0" 376 | resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" 377 | dependencies: 378 | repeating "^2.0.0" 379 | 380 | indexof@~0.0.1: 381 | version "0.0.1" 382 | resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" 383 | 384 | inflight@^1.0.4: 385 | version "1.0.6" 386 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 387 | dependencies: 388 | once "^1.3.0" 389 | wrappy "1" 390 | 391 | inherits@2, inherits@~2.0.3: 392 | version "2.0.3" 393 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 394 | 395 | int64-buffer@^0.1.10: 396 | version "0.1.10" 397 | resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.10.tgz#277b228a87d95ad777d07c13832022406a473423" 398 | 399 | is-arrayish@^0.2.1: 400 | version "0.2.1" 401 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 402 | 403 | is-builtin-module@^1.0.0: 404 | version "1.0.0" 405 | resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" 406 | dependencies: 407 | builtin-modules "^1.0.0" 408 | 409 | is-finite@^1.0.0: 410 | version "1.0.2" 411 | resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" 412 | dependencies: 413 | number-is-nan "^1.0.0" 414 | 415 | is-object@~0.1.2: 416 | version "0.1.2" 417 | resolved "https://registry.yarnpkg.com/is-object/-/is-object-0.1.2.tgz#00efbc08816c33cfc4ac8251d132e10dc65098d7" 418 | 419 | is-plain-obj@^1.1: 420 | version "1.1.0" 421 | resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" 422 | 423 | is-utf8@^0.2.0: 424 | version "0.2.1" 425 | resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" 426 | 427 | is@~0.2.6: 428 | version "0.2.7" 429 | resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562" 430 | 431 | isarray@~1.0.0: 432 | version "1.0.0" 433 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 434 | 435 | isexe@^2.0.0: 436 | version "2.0.0" 437 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 438 | 439 | js-yaml@^3.11.0: 440 | version "3.12.0" 441 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" 442 | dependencies: 443 | argparse "^1.0.7" 444 | esprima "^4.0.0" 445 | 446 | load-json-file@^1.0.0: 447 | version "1.1.0" 448 | resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" 449 | dependencies: 450 | graceful-fs "^4.1.2" 451 | parse-json "^2.2.0" 452 | pify "^2.0.0" 453 | pinkie-promise "^2.0.0" 454 | strip-bom "^2.0.0" 455 | 456 | lodash._arraycopy@^3.0.0: 457 | version "3.0.0" 458 | resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1" 459 | 460 | lodash._arrayeach@^3.0.0: 461 | version "3.0.0" 462 | resolved "https://registry.yarnpkg.com/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz#bab156b2a90d3f1bbd5c653403349e5e5933ef9e" 463 | 464 | lodash._baseassign@^3.0.0: 465 | version "3.2.0" 466 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 467 | dependencies: 468 | lodash._basecopy "^3.0.0" 469 | lodash.keys "^3.0.0" 470 | 471 | lodash._baseclone@^3.0.0: 472 | version "3.3.0" 473 | resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz#303519bf6393fe7e42f34d8b630ef7794e3542b7" 474 | dependencies: 475 | lodash._arraycopy "^3.0.0" 476 | lodash._arrayeach "^3.0.0" 477 | lodash._baseassign "^3.0.0" 478 | lodash._basefor "^3.0.0" 479 | lodash.isarray "^3.0.0" 480 | lodash.keys "^3.0.0" 481 | 482 | lodash._basecopy@^3.0.0: 483 | version "3.0.1" 484 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 485 | 486 | lodash._basefor@^3.0.0: 487 | version "3.0.3" 488 | resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2" 489 | 490 | lodash._bindcallback@^3.0.0: 491 | version "3.0.1" 492 | resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" 493 | 494 | lodash._getnative@^3.0.0: 495 | version "3.9.1" 496 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 497 | 498 | lodash.assign@^4.2.0: 499 | version "4.2.0" 500 | resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" 501 | 502 | lodash.clonedeep@^3.0.0: 503 | version "3.0.2" 504 | resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-3.0.2.tgz#a0a1e40d82a5ea89ff5b147b8444ed63d92827db" 505 | dependencies: 506 | lodash._baseclone "^3.0.0" 507 | lodash._bindcallback "^3.0.0" 508 | 509 | lodash.isarguments@^3.0.0: 510 | version "3.1.0" 511 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 512 | 513 | lodash.isarray@^3.0.0: 514 | version "3.0.4" 515 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 516 | 517 | lodash.keys@^3.0.0: 518 | version "3.1.2" 519 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 520 | dependencies: 521 | lodash._getnative "^3.0.0" 522 | lodash.isarguments "^3.0.0" 523 | lodash.isarray "^3.0.0" 524 | 525 | lodash.toarray@^4.4.0: 526 | version "4.4.0" 527 | resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" 528 | 529 | loud-rejection@^1.0.0: 530 | version "1.6.0" 531 | resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" 532 | dependencies: 533 | currently-unhandled "^0.4.1" 534 | signal-exit "^3.0.0" 535 | 536 | lru-cache@^4.1.3: 537 | version "4.1.3" 538 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" 539 | dependencies: 540 | pseudomap "^1.0.2" 541 | yallist "^2.1.2" 542 | 543 | make-error@^1.1.1: 544 | version "1.3.5" 545 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" 546 | 547 | map-obj@^1.0.0, map-obj@^1.0.1: 548 | version "1.0.1" 549 | resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" 550 | 551 | marked-terminal@^3.0.0: 552 | version "3.1.1" 553 | resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-3.1.1.tgz#1e726816ddc4552a83393228ff0952b6cd4e5e04" 554 | dependencies: 555 | cardinal "^2.1.1" 556 | chalk "^2.4.1" 557 | cli-table "^0.3.1" 558 | lodash.assign "^4.2.0" 559 | node-emoji "^1.4.1" 560 | 561 | marked@^0.5.0: 562 | version "0.5.0" 563 | resolved "https://registry.yarnpkg.com/marked/-/marked-0.5.0.tgz#9e590bad31584a48ff405b33ab1c0dd25172288e" 564 | 565 | meow@^3.3.0: 566 | version "3.7.0" 567 | resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" 568 | dependencies: 569 | camelcase-keys "^2.0.0" 570 | decamelize "^1.1.2" 571 | loud-rejection "^1.0.0" 572 | map-obj "^1.0.1" 573 | minimist "^1.1.3" 574 | normalize-package-data "^2.3.4" 575 | object-assign "^4.0.1" 576 | read-pkg-up "^1.0.1" 577 | redent "^1.0.0" 578 | trim-newlines "^1.0.0" 579 | 580 | merge-options@^1.0.1: 581 | version "1.0.1" 582 | resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-1.0.1.tgz#2a64b24457becd4e4dc608283247e94ce589aa32" 583 | dependencies: 584 | is-plain-obj "^1.1" 585 | 586 | minimatch@^3.0.4: 587 | version "3.0.4" 588 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 589 | dependencies: 590 | brace-expansion "^1.1.7" 591 | 592 | minimist@0.0.8: 593 | version "0.0.8" 594 | resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 595 | 596 | minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: 597 | version "1.2.0" 598 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 599 | 600 | mkdirp@^0.5.1: 601 | version "0.5.1" 602 | resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 603 | dependencies: 604 | minimist "0.0.8" 605 | 606 | nan@^2.4.0, nan@latest: 607 | version "2.10.0" 608 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" 609 | 610 | node-emoji@^1.4.1: 611 | version "1.8.1" 612 | resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.8.1.tgz#6eec6bfb07421e2148c75c6bba72421f8530a826" 613 | dependencies: 614 | lodash.toarray "^4.4.0" 615 | 616 | node-notifier@^4.0.2: 617 | version "4.6.1" 618 | resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-4.6.1.tgz#056d14244f3dcc1ceadfe68af9cff0c5473a33f3" 619 | dependencies: 620 | cli-usage "^0.1.1" 621 | growly "^1.2.0" 622 | lodash.clonedeep "^3.0.0" 623 | minimist "^1.1.1" 624 | semver "^5.1.0" 625 | shellwords "^0.1.0" 626 | which "^1.0.5" 627 | 628 | normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: 629 | version "2.4.0" 630 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" 631 | dependencies: 632 | hosted-git-info "^2.1.4" 633 | is-builtin-module "^1.0.0" 634 | semver "2 || 3 || 4 || 5" 635 | validate-npm-package-license "^3.0.1" 636 | 637 | number-is-nan@^1.0.0: 638 | version "1.0.1" 639 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 640 | 641 | object-assign@^4.0.1: 642 | version "4.1.1" 643 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 644 | 645 | object-keys@~0.2.0: 646 | version "0.2.0" 647 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.2.0.tgz#cddec02998b091be42bf1035ae32e49f1cb6ea67" 648 | dependencies: 649 | foreach "~2.0.1" 650 | indexof "~0.0.1" 651 | is "~0.2.6" 652 | 653 | once@^1.3.0, once@^1.3.1, once@^1.4.0: 654 | version "1.4.0" 655 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 656 | dependencies: 657 | wrappy "1" 658 | 659 | parse-json@^2.2.0: 660 | version "2.2.0" 661 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" 662 | dependencies: 663 | error-ex "^1.2.0" 664 | 665 | path-exists@^2.0.0: 666 | version "2.1.0" 667 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" 668 | dependencies: 669 | pinkie-promise "^2.0.0" 670 | 671 | path-is-absolute@^1.0.0: 672 | version "1.0.1" 673 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 674 | 675 | path-parse@^1.0.5: 676 | version "1.0.6" 677 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 678 | 679 | path-type@^1.0.0: 680 | version "1.1.0" 681 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" 682 | dependencies: 683 | graceful-fs "^4.1.2" 684 | pify "^2.0.0" 685 | pinkie-promise "^2.0.0" 686 | 687 | pify@^2.0.0: 688 | version "2.3.0" 689 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 690 | 691 | pinkie-promise@^2.0.0: 692 | version "2.0.1" 693 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 694 | dependencies: 695 | pinkie "^2.0.0" 696 | 697 | pinkie@^2.0.0: 698 | version "2.0.4" 699 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 700 | 701 | pino-std-serializers@^2.0.0: 702 | version "2.1.0" 703 | resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-2.1.0.tgz#01953dcaecd5f43b331ecf2e312a49c9fd64851c" 704 | 705 | pino@^4.16.1: 706 | version "4.17.3" 707 | resolved "https://registry.yarnpkg.com/pino/-/pino-4.17.3.tgz#3536ea36f6258356ba8891d18dbf1afed41fcf09" 708 | dependencies: 709 | chalk "^2.4.1" 710 | fast-json-parse "^1.0.3" 711 | fast-safe-stringify "^1.2.3" 712 | flatstr "^1.0.5" 713 | pino-std-serializers "^2.0.0" 714 | pump "^3.0.0" 715 | quick-format-unescaped "^1.1.2" 716 | split2 "^2.2.0" 717 | 718 | process-nextick-args@~2.0.0: 719 | version "2.0.0" 720 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" 721 | 722 | pseudomap@^1.0.2: 723 | version "1.0.2" 724 | resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 725 | 726 | pump@^3.0.0: 727 | version "3.0.0" 728 | resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" 729 | dependencies: 730 | end-of-stream "^1.1.0" 731 | once "^1.3.1" 732 | 733 | quick-format-unescaped@^1.1.2: 734 | version "1.1.2" 735 | resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-1.1.2.tgz#0ca581de3174becef25ac3c2e8956342381db698" 736 | dependencies: 737 | fast-safe-stringify "^1.0.8" 738 | 739 | read-pkg-up@^1.0.1: 740 | version "1.0.1" 741 | resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" 742 | dependencies: 743 | find-up "^1.0.0" 744 | read-pkg "^1.0.0" 745 | 746 | read-pkg@^1.0.0: 747 | version "1.1.0" 748 | resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" 749 | dependencies: 750 | load-json-file "^1.0.0" 751 | normalize-package-data "^2.3.2" 752 | path-type "^1.0.0" 753 | 754 | readable-stream@^2.1.5: 755 | version "2.3.6" 756 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 757 | dependencies: 758 | core-util-is "~1.0.0" 759 | inherits "~2.0.3" 760 | isarray "~1.0.0" 761 | process-nextick-args "~2.0.0" 762 | safe-buffer "~5.1.1" 763 | string_decoder "~1.1.1" 764 | util-deprecate "~1.0.1" 765 | 766 | redent@^1.0.0: 767 | version "1.0.0" 768 | resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" 769 | dependencies: 770 | indent-string "^2.1.0" 771 | strip-indent "^1.0.1" 772 | 773 | redeyed@~2.1.0: 774 | version "2.1.1" 775 | resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" 776 | dependencies: 777 | esprima "~4.0.0" 778 | 779 | redis-fast-driver@^2.1.2: 780 | version "2.1.2" 781 | resolved "https://registry.yarnpkg.com/redis-fast-driver/-/redis-fast-driver-2.1.2.tgz#44df8e83bd6aebfcac46d51f207993d4f220e916" 782 | dependencies: 783 | nan latest 784 | 785 | repeating@^2.0.0: 786 | version "2.0.1" 787 | resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" 788 | dependencies: 789 | is-finite "^1.0.0" 790 | 791 | resolve@^1.0.0: 792 | version "1.8.1" 793 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" 794 | dependencies: 795 | path-parse "^1.0.5" 796 | 797 | rimraf@^2.6.1, rimraf@^2.6.2: 798 | version "2.6.2" 799 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" 800 | dependencies: 801 | glob "^7.0.5" 802 | 803 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 804 | version "5.1.2" 805 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 806 | 807 | "semver@2 || 3 || 4 || 5", semver@^5.1.0: 808 | version "5.5.1" 809 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" 810 | 811 | shellwords@^0.1.0: 812 | version "0.1.1" 813 | resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" 814 | 815 | signal-exit@^3.0.0: 816 | version "3.0.2" 817 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 818 | 819 | source-map-support@^0.5.6: 820 | version "0.5.6" 821 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.6.tgz#4435cee46b1aab62b8e8610ce60f788091c51c13" 822 | dependencies: 823 | buffer-from "^1.0.0" 824 | source-map "^0.6.0" 825 | 826 | source-map@^0.6.0: 827 | version "0.6.1" 828 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 829 | 830 | spdx-correct@^3.0.0: 831 | version "3.0.0" 832 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" 833 | dependencies: 834 | spdx-expression-parse "^3.0.0" 835 | spdx-license-ids "^3.0.0" 836 | 837 | spdx-exceptions@^2.1.0: 838 | version "2.1.0" 839 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" 840 | 841 | spdx-expression-parse@^3.0.0: 842 | version "3.0.0" 843 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" 844 | dependencies: 845 | spdx-exceptions "^2.1.0" 846 | spdx-license-ids "^3.0.0" 847 | 848 | spdx-license-ids@^3.0.0: 849 | version "3.0.1" 850 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz#e2a303236cac54b04031fa7a5a79c7e701df852f" 851 | 852 | split2@^2.2.0: 853 | version "2.2.0" 854 | resolved "https://registry.yarnpkg.com/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" 855 | dependencies: 856 | through2 "^2.0.2" 857 | 858 | sprintf-js@~1.0.2: 859 | version "1.0.3" 860 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 861 | 862 | statsd-client@^0.4.2: 863 | version "0.4.2" 864 | resolved "https://registry.yarnpkg.com/statsd-client/-/statsd-client-0.4.2.tgz#c0dc4d583609f97d638742f3ee412c34e9aea482" 865 | 866 | string_decoder@~1.1.1: 867 | version "1.1.1" 868 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 869 | dependencies: 870 | safe-buffer "~5.1.0" 871 | 872 | strip-bom@^2.0.0: 873 | version "2.0.0" 874 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" 875 | dependencies: 876 | is-utf8 "^0.2.0" 877 | 878 | strip-bom@^3.0.0: 879 | version "3.0.0" 880 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 881 | 882 | strip-indent@^1.0.1: 883 | version "1.0.1" 884 | resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" 885 | dependencies: 886 | get-stdin "^4.0.1" 887 | 888 | strip-json-comments@^2.0.0, strip-json-comments@^2.0.1: 889 | version "2.0.1" 890 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 891 | 892 | supports-color@^5.3.0: 893 | version "5.4.0" 894 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 895 | dependencies: 896 | has-flag "^3.0.0" 897 | 898 | through2@^2.0.2: 899 | version "2.0.3" 900 | resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" 901 | dependencies: 902 | readable-stream "^2.1.5" 903 | xtend "~4.0.1" 904 | 905 | trim-newlines@^1.0.0: 906 | version "1.0.0" 907 | resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" 908 | 909 | ts-node-dev@^1.0.0-pre.29: 910 | version "1.0.0-pre.29" 911 | resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.29.tgz#2d2348f57cf5ad9c76f28fa6d9fbfa88f5a5d8bd" 912 | dependencies: 913 | dateformat "~1.0.4-1.2.3" 914 | dynamic-dedupe "^0.2.0" 915 | filewatcher "~3.0.0" 916 | minimist "^1.1.3" 917 | mkdirp "^0.5.1" 918 | node-notifier "^4.0.2" 919 | resolve "^1.0.0" 920 | rimraf "^2.6.1" 921 | ts-node "*" 922 | tsconfig "^7.0.0" 923 | 924 | ts-node@*: 925 | version "7.0.1" 926 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" 927 | dependencies: 928 | arrify "^1.0.0" 929 | buffer-from "^1.1.0" 930 | diff "^3.1.0" 931 | make-error "^1.1.1" 932 | minimist "^1.2.0" 933 | mkdirp "^0.5.1" 934 | source-map-support "^0.5.6" 935 | yn "^2.0.0" 936 | 937 | tsconfig-paths@^3.3.2: 938 | version "3.4.2" 939 | resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.4.2.tgz#4640bffaeee3fc0ab986607edae203859156a8c3" 940 | dependencies: 941 | deepmerge "^2.0.1" 942 | minimist "^1.2.0" 943 | strip-bom "^3.0.0" 944 | strip-json-comments "^2.0.1" 945 | 946 | tsconfig@^7.0.0: 947 | version "7.0.0" 948 | resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" 949 | dependencies: 950 | "@types/strip-bom" "^3.0.0" 951 | "@types/strip-json-comments" "0.0.30" 952 | strip-bom "^3.0.0" 953 | strip-json-comments "^2.0.0" 954 | 955 | typescript@^2.9.1: 956 | version "2.9.2" 957 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" 958 | 959 | util-deprecate@~1.0.1: 960 | version "1.0.2" 961 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 962 | 963 | validate-npm-package-license@^3.0.1: 964 | version "3.0.4" 965 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" 966 | dependencies: 967 | spdx-correct "^3.0.0" 968 | spdx-expression-parse "^3.0.0" 969 | 970 | which@^1.0.5: 971 | version "1.3.1" 972 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 973 | dependencies: 974 | isexe "^2.0.0" 975 | 976 | wrappy@1: 977 | version "1.0.2" 978 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 979 | 980 | xtend@~2.0.6: 981 | version "2.0.6" 982 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.0.6.tgz#5ea657a6dba447069c2e59c58a1138cb0c5e6cee" 983 | dependencies: 984 | is-object "~0.1.2" 985 | object-keys "~0.2.0" 986 | 987 | xtend@~4.0.1: 988 | version "4.0.1" 989 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 990 | 991 | xxhash@^0.2.4: 992 | version "0.2.4" 993 | resolved "https://registry.yarnpkg.com/xxhash/-/xxhash-0.2.4.tgz#8b8a48162cfccc21b920fa500261187d40216c39" 994 | dependencies: 995 | nan "^2.4.0" 996 | 997 | yallist@^2.1.2: 998 | version "2.1.2" 999 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" 1000 | 1001 | yn@^2.0.0: 1002 | version "2.0.0" 1003 | resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" 1004 | --------------------------------------------------------------------------------