├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json ├── src ├── add.js ├── before-exit.js ├── check-plugin.js ├── circuit-breaker.js ├── client-request.js ├── client-response.js ├── codec-pipeline.js ├── constants.js ├── decoder.js ├── encoder.js ├── errors.js ├── extension.js ├── extensions.js ├── handlers.js ├── index.js ├── plugin.js ├── reply.js ├── serializer.js ├── server-request.js ├── server-response.js ├── transport.js └── util.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | lib/** 3 | test/** 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "parser": "babel-eslint", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module", 7 | "ecmaFeatures": { 8 | "impliedStrict": true 9 | } 10 | }, 11 | "rules": { 12 | "semi": ["error", "always"], 13 | "eqeqeq": ["off", "smart"], 14 | "no-console": "off", 15 | "no-unused-vars": "off", 16 | "indent": ["error", 2, { "SwitchCase": 1 }], 17 | "space-before-function-paren": ["error", "always"] 18 | }, 19 | "env": { 20 | "es6": true, 21 | "node": true, 22 | "mocha": true, 23 | "mongo": true 24 | }, 25 | "plugins": [ 26 | "promise" 27 | ] 28 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | .DS_store 30 | 31 | # The compiled/babelified modules 32 | lib/ 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .travis.yml 3 | .istanbul.yml 4 | .babelrc 5 | .idea/ 6 | .vscode/ 7 | .github/ 8 | coverage/ 9 | test/ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 MostlyJS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MostlyJS Microservice on Node.js 2 | ================================ 3 | 4 | [![Build Status](https://travis-ci.org/mostlyjs/mostly-node.svg)](https://travis-ci.org/mostlyjs/mostly-node) 5 | 6 | MostlyJS is a toolkit to develop distributed microservices in a mostly simple way. It uses [NATS](http://nats.io) as the internal communication system for both service discovery and load balancing. NATS is fast and reliable, and is able to handle millions of request per second. 7 | 8 | MostlyJS is targeting breaking current Monolith API application into small services that running on the network transparent to you without knowing where the service physical located. 9 | 10 | It provides well integration with existing node frameworks so that you can change a few code to convert your RESTfull api into microservices. 11 | 12 | * [Express](http://www.expressjs.com) with [express-gateway](https://github.com/MostlyJS/mostly-demos) 13 | * [Feathers](https://feathersjs.com/) with [mostly-feathers](https://github.com/MostlyJS/mostly-feathers) and [mostly-feathers-rest](https://github.com/MostlyJS/mostly-feathers-rest) 14 | * [Poplarjs](https://github.com/poplarjs/poplar) with [mostly-poplarjs](https://github.com/MostlyJS/mostly-poplarjs) and [mostly-poplarjs-rest](https://github.com/MostlyJS/mostly-poplarjs-rest) 15 | 16 | Integration with Koa and Hapi is also planned. 17 | 18 | # Documentation 19 | 20 | Please see the [documentation site](https://mostlyjs.github.io). 21 | 22 | # Usage 23 | 24 | ## Installation 25 | 26 | #### 1. Install Mostly-node and NATS driver 27 | ```bash 28 | npm install nats --save 29 | npm install mostly-node --save 30 | ``` 31 | 32 | #### 2. Install and Run NATS Server 33 | 34 | [https://nats.io/documentation/tutorials/gnatsd-install](https://nats.io/documentation/tutorials/gnatsd-install) 35 | 36 | ## Quick Example 37 | 38 | ```javascript 39 | var mostly = require('mostly-node')() 40 | 41 | // register the service 42 | mostly.add({ topic: 'sample', cmd: 'math' }, function (msg, done) { 43 | var rate = 0.13; 44 | var total = msg.foo * (1 + rate); 45 | done(null, { total: total }); 46 | }); 47 | 48 | // call the service 49 | mostly.act({ topic: 'sample', cmd: 'math', foo: 100 }, function (err, result) { 50 | console.log(result.total); 51 | }); 52 | ``` 53 | 54 | # License 55 | 56 | MIT -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | // Project: https://github.com/MostlyJS/mostly-node 2 | 3 | const events = require('events'); 4 | 5 | declare const Buffer 6 | 7 | declare interface Pattern { 8 | topic: string; 9 | } 10 | 11 | // @TODO use typings 12 | declare interface Promise { 13 | then(onFulfilled? : (value: any) => any) : Promise; 14 | catch(onRejected? : (error: Error) => any) : Promise; 15 | } 16 | 17 | declare interface PluginDefinitionAttributes { 18 | name: string; 19 | description: string; 20 | version: string; 21 | } 22 | 23 | declare interface PluginDefinition { 24 | register: Function; 25 | attributes: PluginDefinitionAttributes; 26 | options: any; 27 | parentPluginName: string; 28 | } 29 | 30 | declare interface Router { 31 | } 32 | 33 | declare interface Load { 34 | } 35 | 36 | declare interface CodecPipeline { 37 | add(step: Function): CodecPipeline; 38 | reset(step: Function): CodecPipeline; 39 | unshift(step: Function): CodecPipeline; 40 | run(msg: string | Buffer, cb: Function): string | Buffer; 41 | } 42 | 43 | declare interface ServerRequest { 44 | payload: any; 45 | error: any; 46 | locals: any; 47 | } 48 | 49 | declare interface ServerResponse { 50 | payload: any; 51 | error: any; 52 | } 53 | 54 | declare interface ClientRequest { 55 | payload: any; 56 | error: any; 57 | } 58 | 59 | declare interface ClientResponse { 60 | payload: any; 61 | error: any; 62 | } 63 | 64 | declare interface Reply { 65 | payload: any; 66 | error: any; 67 | send(data: Error | any); 68 | end(data: Error | any); 69 | } 70 | 71 | declare type RequestType = 72 | 'pubsub' | 73 | 'request' 74 | 75 | declare interface Trace { 76 | traceId: string; 77 | parentSpanId: string; 78 | spanId: string; 79 | timestamp: number; 80 | service: string; 81 | method: string; 82 | duration: number; 83 | } 84 | 85 | declare interface Request { 86 | id: string; 87 | type: RequestType; 88 | } 89 | 90 | declare interface Delegate { 91 | } 92 | 93 | declare interface Meta { 94 | } 95 | 96 | declare type LogLevel = 97 | 'fatal' | 98 | 'error' | 99 | 'warn' | 100 | 'info' | 101 | 'debug' | 102 | 'trace' | 103 | 'silent' 104 | 105 | declare interface ErrioConfig { 106 | recursive: boolean; 107 | inherited: boolean; 108 | stack: boolean; 109 | private: boolean; 110 | exclude: Array; 111 | include: Array; 112 | } 113 | 114 | declare interface BloomrunConfig { 115 | indexing: 'insertion' | 'depth'; 116 | lookupBeforeAdd: boolean; 117 | } 118 | 119 | declare interface LoadConfig { 120 | checkPolicy: boolean; 121 | shouldCrash: boolean; 122 | process: LoadProcessConfig; 123 | policy: LoadPolicyConfig; 124 | } 125 | 126 | declare interface LoadProcessConfig { 127 | sampleInterval: number; 128 | } 129 | 130 | declare interface LoadPolicyConfig { 131 | maxHeapUsedBytes: number; 132 | maxRssBytes: number; 133 | maxEventLoopDelay: number; 134 | } 135 | 136 | declare interface CircuitBreakerConfig { 137 | enabled: boolean; 138 | minSuccesses: number; 139 | halfOpenTime: number; 140 | resetIntervalTime: number; 141 | maxFailures: number; 142 | } 143 | 144 | declare interface Config { 145 | timeout: number; 146 | pluginTimeout: number; 147 | tag: string; 148 | prettyLog: boolean; 149 | name: string; 150 | crashOnFatal: boolean; 151 | logLevel: LogLevel; 152 | childLogger: boolean; 153 | maxRecursion: number; 154 | logger: any; 155 | errio: ErrioConfig; 156 | bloomrun: BloomrunConfig; 157 | load: LoadConfig; 158 | circuitBreaker: CircuitBreakerConfig; 159 | } 160 | 161 | declare type MostlyEvents = 162 | 'error' | 163 | 'clientPreRequest' | 164 | 'clientPostRequest' | 165 | 'serverPreHandler' | 166 | 'serverPreRequest' | 167 | 'serverPreResponse' 168 | 169 | declare type ExtensionType = 170 | 'onClientPreRequest' | 171 | 'onClientPostRequest' | 172 | 'onServerPreHandler' | 173 | 'onServerPreRequest' | 174 | 'onServerPreResponse' 175 | 176 | declare type AddMetaMiddleware = (request: ServerRequest, response: ServerResponse, next: Function) => void; 177 | 178 | declare interface AddMeta { 179 | schema: any; 180 | pattern: Pattern; 181 | action: Function; 182 | plugin: PluginDefinition; 183 | use(handler: AddMetaMiddleware): AddMeta; 184 | end(cb: Function): void; 185 | } 186 | 187 | declare type ClientResult = any; 188 | 189 | declare type AddHandler = (this: MostlyNode, request: Pattern, reply?: Reply) => void; 190 | declare type ActHandler = (this: MostlyNode, error: Error, response: ClientResult) => void; 191 | 192 | declare type ExtensionNextHandler = (error: Error) => void; 193 | declare type ExtensionHandler = (ctx: MostlyNode, request: any, response: any, next?: ExtensionNextHandler) => void; 194 | 195 | interface Plugins { 196 | [name: string]: PluginDefinition; 197 | } 198 | 199 | declare class MostlyNode extends events.EventEmitter { 200 | constructor(transport: object, config: Config); 201 | 202 | ready(callback: Function): void; 203 | act(pattern: string | Pattern, handler?: ActHandler): Promise; 204 | add(pattern: string | Pattern, handler: AddHandler): AddMeta; 205 | use(params: PluginDefinition, options?: any): void; 206 | createError(name: string): any; 207 | decorate(prop: string, value: any): void; 208 | remove(topic: string, maxMessages: number): boolean; 209 | list(Pattern, options: any): any; 210 | close(callback?: Function): void; 211 | fatal(): void; 212 | expose(key: string, object: any): void; 213 | ext(type: ExtensionType, handler: ExtensionHandler): void; 214 | setConfig(key: string, value: any): void; 215 | setOption(key: string, value: any): void; 216 | on(event: MostlyEvents, handler: Function); 217 | removeAll(); 218 | 219 | encoder: CodecPipeline; 220 | decoder: CodecPipeline; 221 | 222 | plugins: Plugins; 223 | router: Router; 224 | load: Load; 225 | exposition: any; 226 | errors: any; 227 | config: any; 228 | topics: any; 229 | transport: any; 230 | 231 | context$: any; 232 | meta$: Meta; 233 | delegate$: Delegate; 234 | auth$: any; 235 | plugin$: PluginDefinition; 236 | trace$: Trace; 237 | request$: Request; 238 | } 239 | 240 | export = MostlyNode 241 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./src/index"); 2 | module.exports.checkPlugin = require('./src/check-plugin'); 3 | module.exports.Errors = require('./src/errors'); 4 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mostly-node", 3 | "version": "1.1.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.0.0-beta.44", 9 | "resolved": "http://registry.npm.taobao.org/@babel/code-frame/download/@babel/code-frame-7.0.0-beta.44.tgz", 10 | "integrity": "sha1-KgJkM2jegJFhYr5whlyXd08629k=", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "7.0.0-beta.44" 14 | } 15 | }, 16 | "@babel/generator": { 17 | "version": "7.0.0-beta.44", 18 | "resolved": "http://registry.npm.taobao.org/@babel/generator/download/@babel/generator-7.0.0-beta.44.tgz", 19 | "integrity": "sha1-x+Z7m1KEr89pswm1DX038+UDPUI=", 20 | "dev": true, 21 | "requires": { 22 | "@babel/types": "7.0.0-beta.44", 23 | "jsesc": "^2.5.1", 24 | "lodash": "^4.2.0", 25 | "source-map": "^0.5.0", 26 | "trim-right": "^1.0.1" 27 | } 28 | }, 29 | "@babel/helper-function-name": { 30 | "version": "7.0.0-beta.44", 31 | "resolved": "http://registry.npm.taobao.org/@babel/helper-function-name/download/@babel/helper-function-name-7.0.0-beta.44.tgz", 32 | "integrity": "sha1-4YVSqq4iMRAKbkheA4VLw1MtRN0=", 33 | "dev": true, 34 | "requires": { 35 | "@babel/helper-get-function-arity": "7.0.0-beta.44", 36 | "@babel/template": "7.0.0-beta.44", 37 | "@babel/types": "7.0.0-beta.44" 38 | } 39 | }, 40 | "@babel/helper-get-function-arity": { 41 | "version": "7.0.0-beta.44", 42 | "resolved": "http://registry.npm.taobao.org/@babel/helper-get-function-arity/download/@babel/helper-get-function-arity-7.0.0-beta.44.tgz", 43 | "integrity": "sha1-0Dym3SufewseazLFbHKDYUDbOhU=", 44 | "dev": true, 45 | "requires": { 46 | "@babel/types": "7.0.0-beta.44" 47 | } 48 | }, 49 | "@babel/helper-split-export-declaration": { 50 | "version": "7.0.0-beta.44", 51 | "resolved": "http://registry.npm.taobao.org/@babel/helper-split-export-declaration/download/@babel/helper-split-export-declaration-7.0.0-beta.44.tgz", 52 | "integrity": "sha1-wLNRc14PvLOCLIrY205YOwXr2dw=", 53 | "dev": true, 54 | "requires": { 55 | "@babel/types": "7.0.0-beta.44" 56 | } 57 | }, 58 | "@babel/highlight": { 59 | "version": "7.0.0-beta.44", 60 | "resolved": "http://registry.npm.taobao.org/@babel/highlight/download/@babel/highlight-7.0.0-beta.44.tgz", 61 | "integrity": "sha1-GMlM5UORaoBVPtzc9oGJCyAHR9U=", 62 | "dev": true, 63 | "requires": { 64 | "chalk": "^2.0.0", 65 | "esutils": "^2.0.2", 66 | "js-tokens": "^3.0.0" 67 | } 68 | }, 69 | "@babel/template": { 70 | "version": "7.0.0-beta.44", 71 | "resolved": "http://registry.npm.taobao.org/@babel/template/download/@babel/template-7.0.0-beta.44.tgz", 72 | "integrity": "sha1-+IMvT9zuXVm/UV5ZX8UQbFKbOU8=", 73 | "dev": true, 74 | "requires": { 75 | "@babel/code-frame": "7.0.0-beta.44", 76 | "@babel/types": "7.0.0-beta.44", 77 | "babylon": "7.0.0-beta.44", 78 | "lodash": "^4.2.0" 79 | } 80 | }, 81 | "@babel/traverse": { 82 | "version": "7.0.0-beta.44", 83 | "resolved": "http://registry.npm.taobao.org/@babel/traverse/download/@babel/traverse-7.0.0-beta.44.tgz", 84 | "integrity": "sha1-qXCixFR3rRgBfi5GWgYG/u4NKWY=", 85 | "dev": true, 86 | "requires": { 87 | "@babel/code-frame": "7.0.0-beta.44", 88 | "@babel/generator": "7.0.0-beta.44", 89 | "@babel/helper-function-name": "7.0.0-beta.44", 90 | "@babel/helper-split-export-declaration": "7.0.0-beta.44", 91 | "@babel/types": "7.0.0-beta.44", 92 | "babylon": "7.0.0-beta.44", 93 | "debug": "^3.1.0", 94 | "globals": "^11.1.0", 95 | "invariant": "^2.2.0", 96 | "lodash": "^4.2.0" 97 | }, 98 | "dependencies": { 99 | "debug": { 100 | "version": "3.1.0", 101 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", 102 | "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", 103 | "dev": true, 104 | "requires": { 105 | "ms": "2.0.0" 106 | } 107 | } 108 | } 109 | }, 110 | "@babel/types": { 111 | "version": "7.0.0-beta.44", 112 | "resolved": "http://registry.npm.taobao.org/@babel/types/download/@babel/types-7.0.0-beta.44.tgz", 113 | "integrity": "sha1-axsWRZH3fewKA0KsqZXy0Eazp1c=", 114 | "dev": true, 115 | "requires": { 116 | "esutils": "^2.0.2", 117 | "lodash": "^4.2.0", 118 | "to-fast-properties": "^2.0.0" 119 | } 120 | }, 121 | "align-text": { 122 | "version": "0.1.4", 123 | "resolved": "http://registry.npm.taobao.org/align-text/download/align-text-0.1.4.tgz", 124 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", 125 | "dev": true, 126 | "optional": true, 127 | "requires": { 128 | "kind-of": "^3.0.2", 129 | "longest": "^1.0.1", 130 | "repeat-string": "^1.5.2" 131 | }, 132 | "dependencies": { 133 | "kind-of": { 134 | "version": "3.2.2", 135 | "resolved": "http://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", 136 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 137 | "dev": true, 138 | "optional": true, 139 | "requires": { 140 | "is-buffer": "^1.1.5" 141 | } 142 | } 143 | } 144 | }, 145 | "amdefine": { 146 | "version": "1.0.1", 147 | "resolved": "http://registry.npm.taobao.org/amdefine/download/amdefine-1.0.1.tgz", 148 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", 149 | "dev": true 150 | }, 151 | "ansi-styles": { 152 | "version": "3.2.1", 153 | "resolved": "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-3.2.1.tgz", 154 | "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", 155 | "requires": { 156 | "color-convert": "^1.9.0" 157 | } 158 | }, 159 | "argparse": { 160 | "version": "1.0.10", 161 | "resolved": "http://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz", 162 | "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", 163 | "dev": true, 164 | "requires": { 165 | "sprintf-js": "~1.0.2" 166 | } 167 | }, 168 | "async": { 169 | "version": "1.5.2", 170 | "resolved": "http://registry.npm.taobao.org/async/download/async-1.5.2.tgz", 171 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 172 | "dev": true 173 | }, 174 | "babel-eslint": { 175 | "version": "8.2.6", 176 | "resolved": "http://registry.npm.taobao.org/babel-eslint/download/babel-eslint-8.2.6.tgz", 177 | "integrity": "sha1-YnDQxzIFYoBnwPeuFpOp55es79k=", 178 | "dev": true, 179 | "requires": { 180 | "@babel/code-frame": "7.0.0-beta.44", 181 | "@babel/traverse": "7.0.0-beta.44", 182 | "@babel/types": "7.0.0-beta.44", 183 | "babylon": "7.0.0-beta.44", 184 | "eslint-scope": "3.7.1", 185 | "eslint-visitor-keys": "^1.0.0" 186 | } 187 | }, 188 | "babylon": { 189 | "version": "7.0.0-beta.44", 190 | "resolved": "http://registry.npm.taobao.org/babylon/download/babylon-7.0.0-beta.44.tgz", 191 | "integrity": "sha1-iRWeFebjDFCW4i1zjYwK+KDoyh0=", 192 | "dev": true 193 | }, 194 | "balanced-match": { 195 | "version": "1.0.0", 196 | "resolved": "http://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz", 197 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 198 | "dev": true 199 | }, 200 | "bloomrun": { 201 | "version": "3.0.6", 202 | "resolved": "http://registry.npm.taobao.org/bloomrun/download/bloomrun-3.0.6.tgz", 203 | "integrity": "sha1-qulPEgsfgyJ1ps4hiMb0BJHxuaI=", 204 | "requires": { 205 | "sorted-array-functions": "^1.0.0" 206 | } 207 | }, 208 | "boom": { 209 | "version": "5.2.0", 210 | "resolved": "http://registry.npm.taobao.org/boom/download/boom-5.2.0.tgz", 211 | "integrity": "sha1-XdnabuOl8wIHdDYpDLcX0/SlTgI=", 212 | "requires": { 213 | "hoek": "4.x.x" 214 | } 215 | }, 216 | "brace-expansion": { 217 | "version": "1.1.11", 218 | "resolved": "http://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz", 219 | "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", 220 | "dev": true, 221 | "requires": { 222 | "balanced-match": "^1.0.0", 223 | "concat-map": "0.0.1" 224 | } 225 | }, 226 | "browser-stdout": { 227 | "version": "1.3.0", 228 | "resolved": "http://registry.npm.taobao.org/browser-stdout/download/browser-stdout-1.3.0.tgz", 229 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 230 | "dev": true 231 | }, 232 | "camelcase": { 233 | "version": "1.2.1", 234 | "resolved": "http://registry.npm.taobao.org/camelcase/download/camelcase-1.2.1.tgz", 235 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", 236 | "dev": true, 237 | "optional": true 238 | }, 239 | "center-align": { 240 | "version": "0.1.3", 241 | "resolved": "http://registry.npm.taobao.org/center-align/download/center-align-0.1.3.tgz", 242 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", 243 | "dev": true, 244 | "optional": true, 245 | "requires": { 246 | "align-text": "^0.1.3", 247 | "lazy-cache": "^1.0.3" 248 | } 249 | }, 250 | "chalk": { 251 | "version": "2.4.1", 252 | "resolved": "http://registry.npm.taobao.org/chalk/download/chalk-2.4.1.tgz", 253 | "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", 254 | "requires": { 255 | "ansi-styles": "^3.2.1", 256 | "escape-string-regexp": "^1.0.5", 257 | "supports-color": "^5.3.0" 258 | } 259 | }, 260 | "cliui": { 261 | "version": "2.1.0", 262 | "resolved": "http://registry.npm.taobao.org/cliui/download/cliui-2.1.0.tgz", 263 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", 264 | "dev": true, 265 | "optional": true, 266 | "requires": { 267 | "center-align": "^0.1.1", 268 | "right-align": "^0.1.1", 269 | "wordwrap": "0.0.2" 270 | }, 271 | "dependencies": { 272 | "wordwrap": { 273 | "version": "0.0.2", 274 | "resolved": "http://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.2.tgz", 275 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", 276 | "dev": true, 277 | "optional": true 278 | } 279 | } 280 | }, 281 | "co": { 282 | "version": "4.6.0", 283 | "resolved": "http://registry.npm.taobao.org/co/download/co-4.6.0.tgz", 284 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 285 | }, 286 | "color-convert": { 287 | "version": "1.9.2", 288 | "resolved": "http://registry.npm.taobao.org/color-convert/download/color-convert-1.9.2.tgz", 289 | "integrity": "sha1-SYgbj7pn3xKpa98/VsCqueeRMUc=", 290 | "requires": { 291 | "color-name": "1.1.1" 292 | } 293 | }, 294 | "color-name": { 295 | "version": "1.1.1", 296 | "resolved": "http://registry.npm.taobao.org/color-name/download/color-name-1.1.1.tgz", 297 | "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" 298 | }, 299 | "commander": { 300 | "version": "2.9.0", 301 | "resolved": "http://registry.npm.taobao.org/commander/download/commander-2.9.0.tgz", 302 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", 303 | "dev": true, 304 | "requires": { 305 | "graceful-readlink": ">= 1.0.0" 306 | } 307 | }, 308 | "concat-map": { 309 | "version": "0.0.1", 310 | "resolved": "http://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz", 311 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 312 | "dev": true 313 | }, 314 | "core-util-is": { 315 | "version": "1.0.2", 316 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 317 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 318 | }, 319 | "debug": { 320 | "version": "2.6.9", 321 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz", 322 | "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", 323 | "requires": { 324 | "ms": "2.0.0" 325 | } 326 | }, 327 | "decamelize": { 328 | "version": "1.2.0", 329 | "resolved": "http://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz", 330 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 331 | "dev": true, 332 | "optional": true 333 | }, 334 | "deep-is": { 335 | "version": "0.1.3", 336 | "resolved": "http://registry.npm.taobao.org/deep-is/download/deep-is-0.1.3.tgz", 337 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 338 | "dev": true 339 | }, 340 | "diff": { 341 | "version": "3.2.0", 342 | "resolved": "http://registry.npm.taobao.org/diff/download/diff-3.2.0.tgz", 343 | "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", 344 | "dev": true 345 | }, 346 | "end-of-stream": { 347 | "version": "1.4.1", 348 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 349 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 350 | "requires": { 351 | "once": "^1.4.0" 352 | } 353 | }, 354 | "errio": { 355 | "version": "1.2.2", 356 | "resolved": "http://registry.npm.taobao.org/errio/download/errio-1.2.2.tgz", 357 | "integrity": "sha1-JDCvpm+m3DY9tYjD2DxEB6hdAlU=" 358 | }, 359 | "escape-string-regexp": { 360 | "version": "1.0.5", 361 | "resolved": "http://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz", 362 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 363 | }, 364 | "escodegen": { 365 | "version": "1.8.1", 366 | "resolved": "http://registry.npm.taobao.org/escodegen/download/escodegen-1.8.1.tgz", 367 | "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", 368 | "dev": true, 369 | "requires": { 370 | "esprima": "^2.7.1", 371 | "estraverse": "^1.9.1", 372 | "esutils": "^2.0.2", 373 | "optionator": "^0.8.1", 374 | "source-map": "~0.2.0" 375 | }, 376 | "dependencies": { 377 | "esprima": { 378 | "version": "2.7.3", 379 | "resolved": "http://registry.npm.taobao.org/esprima/download/esprima-2.7.3.tgz", 380 | "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", 381 | "dev": true 382 | }, 383 | "estraverse": { 384 | "version": "1.9.3", 385 | "resolved": "http://registry.npm.taobao.org/estraverse/download/estraverse-1.9.3.tgz", 386 | "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", 387 | "dev": true 388 | }, 389 | "source-map": { 390 | "version": "0.2.0", 391 | "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.2.0.tgz", 392 | "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", 393 | "dev": true, 394 | "optional": true, 395 | "requires": { 396 | "amdefine": ">=0.0.4" 397 | } 398 | } 399 | } 400 | }, 401 | "eslint-if-supported": { 402 | "version": "1.0.1", 403 | "resolved": "http://registry.npm.taobao.org/eslint-if-supported/download/eslint-if-supported-1.0.1.tgz", 404 | "integrity": "sha1-wqZYOxNKLU2Djq417kuLyNN+hW4=", 405 | "dev": true 406 | }, 407 | "eslint-plugin-promise": { 408 | "version": "3.8.0", 409 | "resolved": "http://registry.npm.taobao.org/eslint-plugin-promise/download/eslint-plugin-promise-3.8.0.tgz", 410 | "integrity": "sha1-ZevyeoRePB6db2pWIt3TgBaUtiE=", 411 | "dev": true 412 | }, 413 | "eslint-scope": { 414 | "version": "3.7.1", 415 | "resolved": "http://registry.npm.taobao.org/eslint-scope/download/eslint-scope-3.7.1.tgz", 416 | "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", 417 | "dev": true, 418 | "requires": { 419 | "esrecurse": "^4.1.0", 420 | "estraverse": "^4.1.1" 421 | } 422 | }, 423 | "eslint-visitor-keys": { 424 | "version": "1.0.0", 425 | "resolved": "http://registry.npm.taobao.org/eslint-visitor-keys/download/eslint-visitor-keys-1.0.0.tgz", 426 | "integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=", 427 | "dev": true 428 | }, 429 | "esprima": { 430 | "version": "4.0.1", 431 | "resolved": "http://registry.npm.taobao.org/esprima/download/esprima-4.0.1.tgz", 432 | "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", 433 | "dev": true 434 | }, 435 | "esrecurse": { 436 | "version": "4.2.1", 437 | "resolved": "http://registry.npm.taobao.org/esrecurse/download/esrecurse-4.2.1.tgz", 438 | "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", 439 | "dev": true, 440 | "requires": { 441 | "estraverse": "^4.1.0" 442 | } 443 | }, 444 | "estraverse": { 445 | "version": "4.2.0", 446 | "resolved": "http://registry.npm.taobao.org/estraverse/download/estraverse-4.2.0.tgz", 447 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", 448 | "dev": true 449 | }, 450 | "esutils": { 451 | "version": "2.0.2", 452 | "resolved": "http://registry.npm.taobao.org/esutils/download/esutils-2.0.2.tgz", 453 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 454 | "dev": true 455 | }, 456 | "fast-json-parse": { 457 | "version": "1.0.3", 458 | "resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz", 459 | "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==" 460 | }, 461 | "fast-levenshtein": { 462 | "version": "2.0.6", 463 | "resolved": "http://registry.npm.taobao.org/fast-levenshtein/download/fast-levenshtein-2.0.6.tgz", 464 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 465 | "dev": true 466 | }, 467 | "fast-safe-stringify": { 468 | "version": "1.2.3", 469 | "resolved": "http://registry.npm.taobao.org/fast-safe-stringify/download/fast-safe-stringify-1.2.3.tgz", 470 | "integrity": "sha1-n+IsN/svf4bwa48AQ3fb+PHue8E=" 471 | }, 472 | "flatstr": { 473 | "version": "1.0.12", 474 | "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz", 475 | "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" 476 | }, 477 | "fs.realpath": { 478 | "version": "1.0.0", 479 | "resolved": "http://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz", 480 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 481 | "dev": true 482 | }, 483 | "globals": { 484 | "version": "11.7.0", 485 | "resolved": "http://registry.npm.taobao.org/globals/download/globals-11.7.0.tgz", 486 | "integrity": "sha1-pYP6pDBVsayncZFL9oJY4vwSVnM=", 487 | "dev": true 488 | }, 489 | "graceful-readlink": { 490 | "version": "1.0.1", 491 | "resolved": "http://registry.npm.taobao.org/graceful-readlink/download/graceful-readlink-1.0.1.tgz", 492 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", 493 | "dev": true 494 | }, 495 | "growl": { 496 | "version": "1.9.2", 497 | "resolved": "http://registry.npm.taobao.org/growl/download/growl-1.9.2.tgz", 498 | "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", 499 | "dev": true 500 | }, 501 | "handlebars": { 502 | "version": "4.0.11", 503 | "resolved": "http://registry.npm.taobao.org/handlebars/download/handlebars-4.0.11.tgz", 504 | "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", 505 | "dev": true, 506 | "requires": { 507 | "async": "^1.4.0", 508 | "optimist": "^0.6.1", 509 | "source-map": "^0.4.4", 510 | "uglify-js": "^2.6" 511 | }, 512 | "dependencies": { 513 | "source-map": { 514 | "version": "0.4.4", 515 | "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.4.4.tgz", 516 | "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", 517 | "dev": true, 518 | "requires": { 519 | "amdefine": ">=0.0.4" 520 | } 521 | } 522 | } 523 | }, 524 | "has-flag": { 525 | "version": "3.0.0", 526 | "resolved": "http://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz", 527 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 528 | }, 529 | "he": { 530 | "version": "1.1.1", 531 | "resolved": "http://registry.npm.taobao.org/he/download/he-1.1.1.tgz", 532 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 533 | "dev": true 534 | }, 535 | "heavy": { 536 | "version": "4.0.4", 537 | "resolved": "http://registry.npm.taobao.org/heavy/download/heavy-4.0.4.tgz", 538 | "integrity": "sha1-NskTNsAMz+hSyqTRUwhjNc0vAOk=", 539 | "requires": { 540 | "boom": "5.x.x", 541 | "hoek": "4.x.x", 542 | "joi": "10.x.x" 543 | } 544 | }, 545 | "hoek": { 546 | "version": "4.2.1", 547 | "resolved": "http://registry.npm.taobao.org/hoek/download/hoek-4.2.1.tgz", 548 | "integrity": "sha1-ljRQKqEsRF3Vp8VzS1cruHOKrLs=" 549 | }, 550 | "inflight": { 551 | "version": "1.0.6", 552 | "resolved": "http://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz", 553 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 554 | "dev": true, 555 | "requires": { 556 | "once": "^1.3.0", 557 | "wrappy": "1" 558 | } 559 | }, 560 | "inherits": { 561 | "version": "2.0.3", 562 | "resolved": "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", 563 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 564 | }, 565 | "invariant": { 566 | "version": "2.2.4", 567 | "resolved": "http://registry.npm.taobao.org/invariant/download/invariant-2.2.4.tgz", 568 | "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", 569 | "dev": true, 570 | "requires": { 571 | "loose-envify": "^1.0.0" 572 | } 573 | }, 574 | "is-buffer": { 575 | "version": "1.1.6", 576 | "resolved": "http://registry.npm.taobao.org/is-buffer/download/is-buffer-1.1.6.tgz", 577 | "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", 578 | "dev": true, 579 | "optional": true 580 | }, 581 | "isarray": { 582 | "version": "1.0.0", 583 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 584 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 585 | }, 586 | "isemail": { 587 | "version": "2.2.1", 588 | "resolved": "http://registry.npm.taobao.org/isemail/download/isemail-2.2.1.tgz", 589 | "integrity": "sha1-A1PT2aYpUQgMJiwqoKQrjqjp4qY=" 590 | }, 591 | "isexe": { 592 | "version": "2.0.0", 593 | "resolved": "http://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz", 594 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 595 | "dev": true 596 | }, 597 | "istanbul": { 598 | "version": "0.4.5", 599 | "resolved": "http://registry.npm.taobao.org/istanbul/download/istanbul-0.4.5.tgz", 600 | "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", 601 | "dev": true, 602 | "requires": { 603 | "abbrev": "1.0.x", 604 | "async": "1.x", 605 | "escodegen": "1.8.x", 606 | "esprima": "2.7.x", 607 | "glob": "^5.0.15", 608 | "handlebars": "^4.0.1", 609 | "js-yaml": "3.x", 610 | "mkdirp": "0.5.x", 611 | "nopt": "3.x", 612 | "once": "1.x", 613 | "resolve": "1.1.x", 614 | "supports-color": "^3.1.0", 615 | "which": "^1.1.1", 616 | "wordwrap": "^1.0.0" 617 | }, 618 | "dependencies": { 619 | "abbrev": { 620 | "version": "1.0.9", 621 | "resolved": "http://registry.npm.taobao.org/abbrev/download/abbrev-1.0.9.tgz", 622 | "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", 623 | "dev": true 624 | }, 625 | "esprima": { 626 | "version": "2.7.3", 627 | "resolved": "http://registry.npm.taobao.org/esprima/download/esprima-2.7.3.tgz", 628 | "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", 629 | "dev": true 630 | }, 631 | "glob": { 632 | "version": "5.0.15", 633 | "resolved": "http://registry.npm.taobao.org/glob/download/glob-5.0.15.tgz", 634 | "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", 635 | "dev": true, 636 | "requires": { 637 | "inflight": "^1.0.4", 638 | "inherits": "2", 639 | "minimatch": "2 || 3", 640 | "once": "^1.3.0", 641 | "path-is-absolute": "^1.0.0" 642 | } 643 | }, 644 | "has-flag": { 645 | "version": "1.0.0", 646 | "resolved": "http://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", 647 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", 648 | "dev": true 649 | }, 650 | "nopt": { 651 | "version": "3.0.6", 652 | "resolved": "http://registry.npm.taobao.org/nopt/download/nopt-3.0.6.tgz", 653 | "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", 654 | "dev": true, 655 | "requires": { 656 | "abbrev": "1" 657 | } 658 | }, 659 | "resolve": { 660 | "version": "1.1.7", 661 | "resolved": "http://registry.npm.taobao.org/resolve/download/resolve-1.1.7.tgz", 662 | "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", 663 | "dev": true 664 | }, 665 | "supports-color": { 666 | "version": "3.2.3", 667 | "resolved": "http://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz", 668 | "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", 669 | "dev": true, 670 | "requires": { 671 | "has-flag": "^1.0.0" 672 | } 673 | } 674 | } 675 | }, 676 | "items": { 677 | "version": "2.1.1", 678 | "resolved": "http://registry.npm.taobao.org/items/download/items-2.1.1.tgz", 679 | "integrity": "sha1-i9FtnIOxlSneWuoyGsqtp4NkoZg=" 680 | }, 681 | "joi": { 682 | "version": "10.6.0", 683 | "resolved": "http://registry.npm.taobao.org/joi/download/joi-10.6.0.tgz", 684 | "integrity": "sha1-Ulh/AtUri3XNsMdPCxZKGRoOH8I=", 685 | "requires": { 686 | "hoek": "4.x.x", 687 | "isemail": "2.x.x", 688 | "items": "2.x.x", 689 | "topo": "2.x.x" 690 | } 691 | }, 692 | "js-tokens": { 693 | "version": "3.0.2", 694 | "resolved": "http://registry.npm.taobao.org/js-tokens/download/js-tokens-3.0.2.tgz", 695 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", 696 | "dev": true 697 | }, 698 | "js-yaml": { 699 | "version": "3.12.0", 700 | "resolved": "http://registry.npm.taobao.org/js-yaml/download/js-yaml-3.12.0.tgz", 701 | "integrity": "sha1-6u1lbsg0TxD1J8a/obbiJE3hZ9E=", 702 | "dev": true, 703 | "requires": { 704 | "argparse": "^1.0.7", 705 | "esprima": "^4.0.0" 706 | } 707 | }, 708 | "jsesc": { 709 | "version": "2.5.1", 710 | "resolved": "http://registry.npm.taobao.org/jsesc/download/jsesc-2.5.1.tgz", 711 | "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", 712 | "dev": true 713 | }, 714 | "json3": { 715 | "version": "3.3.2", 716 | "resolved": "http://registry.npm.taobao.org/json3/download/json3-3.3.2.tgz", 717 | "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", 718 | "dev": true 719 | }, 720 | "lazy-cache": { 721 | "version": "1.0.4", 722 | "resolved": "http://registry.npm.taobao.org/lazy-cache/download/lazy-cache-1.0.4.tgz", 723 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", 724 | "dev": true, 725 | "optional": true 726 | }, 727 | "levn": { 728 | "version": "0.3.0", 729 | "resolved": "http://registry.npm.taobao.org/levn/download/levn-0.3.0.tgz", 730 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 731 | "dev": true, 732 | "requires": { 733 | "prelude-ls": "~1.1.2", 734 | "type-check": "~0.3.2" 735 | } 736 | }, 737 | "lodash": { 738 | "version": "4.17.10", 739 | "resolved": "http://registry.npm.taobao.org/lodash/download/lodash-4.17.10.tgz", 740 | "integrity": "sha1-G3eTz3JZ6jj7NmHU04syYK+K5Oc=" 741 | }, 742 | "lodash._baseassign": { 743 | "version": "3.2.0", 744 | "resolved": "http://registry.npm.taobao.org/lodash._baseassign/download/lodash._baseassign-3.2.0.tgz", 745 | "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", 746 | "dev": true, 747 | "requires": { 748 | "lodash._basecopy": "^3.0.0", 749 | "lodash.keys": "^3.0.0" 750 | } 751 | }, 752 | "lodash._basecopy": { 753 | "version": "3.0.1", 754 | "resolved": "http://registry.npm.taobao.org/lodash._basecopy/download/lodash._basecopy-3.0.1.tgz", 755 | "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", 756 | "dev": true 757 | }, 758 | "lodash._basecreate": { 759 | "version": "3.0.3", 760 | "resolved": "http://registry.npm.taobao.org/lodash._basecreate/download/lodash._basecreate-3.0.3.tgz", 761 | "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", 762 | "dev": true 763 | }, 764 | "lodash._getnative": { 765 | "version": "3.9.1", 766 | "resolved": "http://registry.npm.taobao.org/lodash._getnative/download/lodash._getnative-3.9.1.tgz", 767 | "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", 768 | "dev": true 769 | }, 770 | "lodash._isiterateecall": { 771 | "version": "3.0.9", 772 | "resolved": "http://registry.npm.taobao.org/lodash._isiterateecall/download/lodash._isiterateecall-3.0.9.tgz", 773 | "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", 774 | "dev": true 775 | }, 776 | "lodash.create": { 777 | "version": "3.1.1", 778 | "resolved": "http://registry.npm.taobao.org/lodash.create/download/lodash.create-3.1.1.tgz", 779 | "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", 780 | "dev": true, 781 | "requires": { 782 | "lodash._baseassign": "^3.0.0", 783 | "lodash._basecreate": "^3.0.0", 784 | "lodash._isiterateecall": "^3.0.0" 785 | } 786 | }, 787 | "lodash.isarguments": { 788 | "version": "3.1.0", 789 | "resolved": "http://registry.npm.taobao.org/lodash.isarguments/download/lodash.isarguments-3.1.0.tgz", 790 | "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", 791 | "dev": true 792 | }, 793 | "lodash.isarray": { 794 | "version": "3.0.4", 795 | "resolved": "http://registry.npm.taobao.org/lodash.isarray/download/lodash.isarray-3.0.4.tgz", 796 | "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", 797 | "dev": true 798 | }, 799 | "lodash.keys": { 800 | "version": "3.1.2", 801 | "resolved": "http://registry.npm.taobao.org/lodash.keys/download/lodash.keys-3.1.2.tgz", 802 | "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", 803 | "dev": true, 804 | "requires": { 805 | "lodash._getnative": "^3.0.0", 806 | "lodash.isarguments": "^3.0.0", 807 | "lodash.isarray": "^3.0.0" 808 | } 809 | }, 810 | "longest": { 811 | "version": "1.0.1", 812 | "resolved": "http://registry.npm.taobao.org/longest/download/longest-1.0.1.tgz", 813 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", 814 | "dev": true, 815 | "optional": true 816 | }, 817 | "loose-envify": { 818 | "version": "1.4.0", 819 | "resolved": "http://registry.npm.taobao.org/loose-envify/download/loose-envify-1.4.0.tgz", 820 | "integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=", 821 | "dev": true, 822 | "requires": { 823 | "js-tokens": "^3.0.0 || ^4.0.0" 824 | } 825 | }, 826 | "minimatch": { 827 | "version": "2.0.10", 828 | "resolved": "http://registry.npm.taobao.org/minimatch/download/minimatch-2.0.10.tgz", 829 | "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", 830 | "dev": true, 831 | "requires": { 832 | "brace-expansion": "^1.0.0" 833 | } 834 | }, 835 | "mkdirp": { 836 | "version": "0.5.1", 837 | "resolved": "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz", 838 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 839 | "dev": true, 840 | "requires": { 841 | "minimist": "0.0.8" 842 | }, 843 | "dependencies": { 844 | "minimist": { 845 | "version": "0.0.8", 846 | "resolved": "http://registry.npm.taobao.org/minimist/download/minimist-0.0.8.tgz", 847 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 848 | "dev": true 849 | } 850 | } 851 | }, 852 | "mocha": { 853 | "version": "3.5.3", 854 | "resolved": "http://registry.npm.taobao.org/mocha/download/mocha-3.5.3.tgz", 855 | "integrity": "sha1-HgSA/jbS2lhY0etqzDhBiybqog0=", 856 | "dev": true, 857 | "requires": { 858 | "browser-stdout": "1.3.0", 859 | "commander": "2.9.0", 860 | "debug": "2.6.8", 861 | "diff": "3.2.0", 862 | "escape-string-regexp": "1.0.5", 863 | "glob": "7.1.1", 864 | "growl": "1.9.2", 865 | "he": "1.1.1", 866 | "json3": "3.3.2", 867 | "lodash.create": "3.1.1", 868 | "mkdirp": "0.5.1", 869 | "supports-color": "3.1.2" 870 | }, 871 | "dependencies": { 872 | "debug": { 873 | "version": "2.6.8", 874 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.8.tgz", 875 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", 876 | "dev": true, 877 | "requires": { 878 | "ms": "2.0.0" 879 | } 880 | }, 881 | "glob": { 882 | "version": "7.1.1", 883 | "resolved": "http://registry.npm.taobao.org/glob/download/glob-7.1.1.tgz", 884 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 885 | "dev": true, 886 | "requires": { 887 | "fs.realpath": "^1.0.0", 888 | "inflight": "^1.0.4", 889 | "inherits": "2", 890 | "minimatch": "^3.0.2", 891 | "once": "^1.3.0", 892 | "path-is-absolute": "^1.0.0" 893 | } 894 | }, 895 | "has-flag": { 896 | "version": "1.0.0", 897 | "resolved": "http://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", 898 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", 899 | "dev": true 900 | }, 901 | "minimatch": { 902 | "version": "3.0.4", 903 | "resolved": "http://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz", 904 | "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", 905 | "dev": true, 906 | "requires": { 907 | "brace-expansion": "^1.1.7" 908 | } 909 | }, 910 | "supports-color": { 911 | "version": "3.1.2", 912 | "resolved": "http://registry.npm.taobao.org/supports-color/download/supports-color-3.1.2.tgz", 913 | "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", 914 | "dev": true, 915 | "requires": { 916 | "has-flag": "^1.0.0" 917 | } 918 | } 919 | } 920 | }, 921 | "ms": { 922 | "version": "2.0.0", 923 | "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz", 924 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 925 | }, 926 | "nats": { 927 | "version": "1.0.1", 928 | "resolved": "http://registry.npm.taobao.org/nats/download/nats-1.0.1.tgz", 929 | "integrity": "sha1-4b0VYaIBiPms6y0nUSf6y8diwpI=", 930 | "requires": { 931 | "nuid": "^1.0.0" 932 | } 933 | }, 934 | "nuid": { 935 | "version": "1.0.0", 936 | "resolved": "http://registry.npm.taobao.org/nuid/download/nuid-1.0.0.tgz", 937 | "integrity": "sha1-BOzCG+QcwyGUCpCJmEOC873GFjg=" 938 | }, 939 | "once": { 940 | "version": "1.4.0", 941 | "resolved": "http://registry.npm.taobao.org/once/download/once-1.4.0.tgz", 942 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 943 | "requires": { 944 | "wrappy": "1" 945 | } 946 | }, 947 | "optimist": { 948 | "version": "0.6.1", 949 | "resolved": "http://registry.npm.taobao.org/optimist/download/optimist-0.6.1.tgz", 950 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 951 | "dev": true, 952 | "requires": { 953 | "minimist": "~0.0.1", 954 | "wordwrap": "~0.0.2" 955 | }, 956 | "dependencies": { 957 | "minimist": { 958 | "version": "0.0.10", 959 | "resolved": "http://registry.npm.taobao.org/minimist/download/minimist-0.0.10.tgz", 960 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", 961 | "dev": true 962 | }, 963 | "wordwrap": { 964 | "version": "0.0.3", 965 | "resolved": "http://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.3.tgz", 966 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", 967 | "dev": true 968 | } 969 | } 970 | }, 971 | "optionator": { 972 | "version": "0.8.2", 973 | "resolved": "http://registry.npm.taobao.org/optionator/download/optionator-0.8.2.tgz", 974 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 975 | "dev": true, 976 | "requires": { 977 | "deep-is": "~0.1.3", 978 | "fast-levenshtein": "~2.0.4", 979 | "levn": "~0.3.0", 980 | "prelude-ls": "~1.1.2", 981 | "type-check": "~0.3.2", 982 | "wordwrap": "~1.0.0" 983 | } 984 | }, 985 | "path-is-absolute": { 986 | "version": "1.0.1", 987 | "resolved": "http://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz", 988 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 989 | "dev": true 990 | }, 991 | "pino": { 992 | "version": "4.17.6", 993 | "resolved": "https://registry.npmjs.org/pino/-/pino-4.17.6.tgz", 994 | "integrity": "sha512-LFDwmhyWLBnmwO/2UFbWu1jEGVDzaPupaVdx0XcZ3tIAx1EDEBauzxXf2S0UcFK7oe+X9MApjH0hx9U1XMgfCA==", 995 | "requires": { 996 | "chalk": "^2.4.1", 997 | "fast-json-parse": "^1.0.3", 998 | "fast-safe-stringify": "^1.2.3", 999 | "flatstr": "^1.0.5", 1000 | "pino-std-serializers": "^2.0.0", 1001 | "pump": "^3.0.0", 1002 | "quick-format-unescaped": "^1.1.2", 1003 | "split2": "^2.2.0" 1004 | } 1005 | }, 1006 | "pino-std-serializers": { 1007 | "version": "2.4.1", 1008 | "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.4.1.tgz", 1009 | "integrity": "sha512-v/JglhO0aFcvkMV9VUxhgyuJo8K1si857Ww86Tx8H2cjC/kp0ndzzcF6Vbxr4RgKFYJdHfLVpEuD55znMZuxnw==" 1010 | }, 1011 | "prelude-ls": { 1012 | "version": "1.1.2", 1013 | "resolved": "http://registry.npm.taobao.org/prelude-ls/download/prelude-ls-1.1.2.tgz", 1014 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 1015 | "dev": true 1016 | }, 1017 | "process-nextick-args": { 1018 | "version": "2.0.0", 1019 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1020 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 1021 | }, 1022 | "pump": { 1023 | "version": "3.0.0", 1024 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 1025 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 1026 | "requires": { 1027 | "end-of-stream": "^1.1.0", 1028 | "once": "^1.3.1" 1029 | } 1030 | }, 1031 | "quick-format-unescaped": { 1032 | "version": "1.1.2", 1033 | "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-1.1.2.tgz", 1034 | "integrity": "sha1-DKWB3jF0vs7yWsPC6JVjQjgdtpg=", 1035 | "requires": { 1036 | "fast-safe-stringify": "^1.0.8" 1037 | } 1038 | }, 1039 | "readable-stream": { 1040 | "version": "2.3.6", 1041 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1042 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1043 | "requires": { 1044 | "core-util-is": "~1.0.0", 1045 | "inherits": "~2.0.3", 1046 | "isarray": "~1.0.0", 1047 | "process-nextick-args": "~2.0.0", 1048 | "safe-buffer": "~5.1.1", 1049 | "string_decoder": "~1.1.1", 1050 | "util-deprecate": "~1.0.1" 1051 | } 1052 | }, 1053 | "repeat-string": { 1054 | "version": "1.6.1", 1055 | "resolved": "http://registry.npm.taobao.org/repeat-string/download/repeat-string-1.6.1.tgz", 1056 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", 1057 | "dev": true, 1058 | "optional": true 1059 | }, 1060 | "right-align": { 1061 | "version": "0.1.3", 1062 | "resolved": "http://registry.npm.taobao.org/right-align/download/right-align-0.1.3.tgz", 1063 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 1064 | "dev": true, 1065 | "optional": true, 1066 | "requires": { 1067 | "align-text": "^0.1.1" 1068 | } 1069 | }, 1070 | "safe-buffer": { 1071 | "version": "5.1.2", 1072 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1073 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1074 | }, 1075 | "semver": { 1076 | "version": "5.5.0", 1077 | "resolved": "http://registry.npm.taobao.org/semver/download/semver-5.5.0.tgz", 1078 | "integrity": "sha1-3Eu8emyp2Rbe5dQ1FvAJK1j3uKs=" 1079 | }, 1080 | "sorted-array-functions": { 1081 | "version": "1.2.0", 1082 | "resolved": "http://registry.npm.taobao.org/sorted-array-functions/download/sorted-array-functions-1.2.0.tgz", 1083 | "integrity": "sha1-QyZbIdbphbffMWIbHBHMaNjvx8M=" 1084 | }, 1085 | "source-map": { 1086 | "version": "0.5.7", 1087 | "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", 1088 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 1089 | "dev": true 1090 | }, 1091 | "split2": { 1092 | "version": "2.2.0", 1093 | "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", 1094 | "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", 1095 | "requires": { 1096 | "through2": "^2.0.2" 1097 | } 1098 | }, 1099 | "sprintf-js": { 1100 | "version": "1.0.3", 1101 | "resolved": "http://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz", 1102 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1103 | "dev": true 1104 | }, 1105 | "string_decoder": { 1106 | "version": "1.1.1", 1107 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1108 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1109 | "requires": { 1110 | "safe-buffer": "~5.1.0" 1111 | } 1112 | }, 1113 | "super-error": { 1114 | "version": "2.2.0", 1115 | "resolved": "http://registry.npm.taobao.org/super-error/download/super-error-2.2.0.tgz", 1116 | "integrity": "sha1-2FDPSJn6eIDi82ZB4wHDl7DqoQg=" 1117 | }, 1118 | "supports-color": { 1119 | "version": "5.4.0", 1120 | "resolved": "http://registry.npm.taobao.org/supports-color/download/supports-color-5.4.0.tgz", 1121 | "integrity": "sha1-HGszdALCE3YF7+GfEP7DkPb6q1Q=", 1122 | "requires": { 1123 | "has-flag": "^3.0.0" 1124 | } 1125 | }, 1126 | "through2": { 1127 | "version": "2.0.5", 1128 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", 1129 | "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", 1130 | "requires": { 1131 | "readable-stream": "~2.3.6", 1132 | "xtend": "~4.0.1" 1133 | } 1134 | }, 1135 | "tinysonic": { 1136 | "version": "1.3.0", 1137 | "resolved": "http://registry.npm.taobao.org/tinysonic/download/tinysonic-1.3.0.tgz", 1138 | "integrity": "sha1-P0U2OFAcCZ21nRej9cHipKnG2Fo=" 1139 | }, 1140 | "to-fast-properties": { 1141 | "version": "2.0.0", 1142 | "resolved": "http://registry.npm.taobao.org/to-fast-properties/download/to-fast-properties-2.0.0.tgz", 1143 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", 1144 | "dev": true 1145 | }, 1146 | "topo": { 1147 | "version": "2.0.2", 1148 | "resolved": "http://registry.npm.taobao.org/topo/download/topo-2.0.2.tgz", 1149 | "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=", 1150 | "requires": { 1151 | "hoek": "4.x.x" 1152 | } 1153 | }, 1154 | "trim-right": { 1155 | "version": "1.0.1", 1156 | "resolved": "http://registry.npm.taobao.org/trim-right/download/trim-right-1.0.1.tgz", 1157 | "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", 1158 | "dev": true 1159 | }, 1160 | "type-check": { 1161 | "version": "0.3.2", 1162 | "resolved": "http://registry.npm.taobao.org/type-check/download/type-check-0.3.2.tgz", 1163 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1164 | "dev": true, 1165 | "requires": { 1166 | "prelude-ls": "~1.1.2" 1167 | } 1168 | }, 1169 | "uglify-js": { 1170 | "version": "2.8.29", 1171 | "resolved": "http://registry.npm.taobao.org/uglify-js/download/uglify-js-2.8.29.tgz", 1172 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 1173 | "dev": true, 1174 | "optional": true, 1175 | "requires": { 1176 | "source-map": "~0.5.1", 1177 | "uglify-to-browserify": "~1.0.0", 1178 | "yargs": "~3.10.0" 1179 | } 1180 | }, 1181 | "uglify-to-browserify": { 1182 | "version": "1.0.2", 1183 | "resolved": "http://registry.npm.taobao.org/uglify-to-browserify/download/uglify-to-browserify-1.0.2.tgz", 1184 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 1185 | "dev": true, 1186 | "optional": true 1187 | }, 1188 | "util-deprecate": { 1189 | "version": "1.0.2", 1190 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1191 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1192 | }, 1193 | "which": { 1194 | "version": "1.3.1", 1195 | "resolved": "http://registry.npm.taobao.org/which/download/which-1.3.1.tgz", 1196 | "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", 1197 | "dev": true, 1198 | "requires": { 1199 | "isexe": "^2.0.0" 1200 | } 1201 | }, 1202 | "window-size": { 1203 | "version": "0.1.0", 1204 | "resolved": "http://registry.npm.taobao.org/window-size/download/window-size-0.1.0.tgz", 1205 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", 1206 | "dev": true, 1207 | "optional": true 1208 | }, 1209 | "wordwrap": { 1210 | "version": "1.0.0", 1211 | "resolved": "http://registry.npm.taobao.org/wordwrap/download/wordwrap-1.0.0.tgz", 1212 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1213 | "dev": true 1214 | }, 1215 | "wrappy": { 1216 | "version": "1.0.2", 1217 | "resolved": "http://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz", 1218 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1219 | }, 1220 | "xtend": { 1221 | "version": "4.0.1", 1222 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 1223 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 1224 | }, 1225 | "yargs": { 1226 | "version": "3.10.0", 1227 | "resolved": "http://registry.npm.taobao.org/yargs/download/yargs-3.10.0.tgz", 1228 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 1229 | "dev": true, 1230 | "optional": true, 1231 | "requires": { 1232 | "camelcase": "^1.0.2", 1233 | "cliui": "^2.1.0", 1234 | "decamelize": "^1.0.0", 1235 | "window-size": "0.1.0" 1236 | } 1237 | } 1238 | } 1239 | } 1240 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mostly-node", 3 | "version": "1.1.1", 4 | "description": "MostlyJS microservice on Node.js", 5 | "author": { 6 | "name": "mostly", 7 | "email": "hello@playingio.com", 8 | "url": "https://mostlyjs.github.com" 9 | }, 10 | "license": "MIT", 11 | "main": "index.js", 12 | "scripts": { 13 | "publish": "git push origin --tags && git push origin", 14 | "release:patch": "npm version patch && npm publish", 15 | "release:minor": "npm version minor && npm publish", 16 | "release:major": "npm version major && npm publish", 17 | "lint": "eslint-if-supported semistandard --fix", 18 | "mocha": "mocha --opts mocha.opts", 19 | "coverage": "istanbul cover node_modules/mocha/bin/_mocha -- --opts mocha.opts", 20 | "test": "npm run lint && npm run coverage" 21 | }, 22 | "homepage": "https://github.com/MostlyJS/mostly-node#readme", 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/MostlyJS/mostly-node.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/MostlyJS/mostly-node/issues" 29 | }, 30 | "dependencies": { 31 | "bloomrun": "^3.0.4", 32 | "co": "^4.6.0", 33 | "debug": "^2.6.6", 34 | "errio": "^1.2.2", 35 | "fast-safe-stringify": "^1.1.13", 36 | "heavy": "^4.0.3", 37 | "lodash": "^4.17.4", 38 | "nats": "^1.0.0", 39 | "pino": "^4.17.6", 40 | "semver": "^5.4.1", 41 | "super-error": "^2.0.0", 42 | "tinysonic": "^1.2.0" 43 | }, 44 | "devDependencies": { 45 | "babel-eslint": "^8.2.2", 46 | "eslint-if-supported": "^1.0.1", 47 | "eslint-plugin-promise": "^3.5.0", 48 | "istanbul": "^0.4.5", 49 | "mocha": "^3.3.0" 50 | }, 51 | "typings": "./index.d.ts" 52 | } 53 | -------------------------------------------------------------------------------- /src/add.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Co = require('co'); 3 | const Util = require('./util'); 4 | 5 | class Add { 6 | 7 | constructor (actMeta, options) { 8 | this.actMeta = actMeta; 9 | this.options = options; 10 | this.isPromisable = false; 11 | this.actMeta.middleware = actMeta.middleware || []; 12 | } 13 | 14 | _use (handler) { 15 | this.actMeta.middleware.push(Util.toPromiseFact(handler)); 16 | } 17 | 18 | use (handler) { 19 | if (_.isArray(handler)) { 20 | handler.forEach(h => this._use(h)); 21 | } else { 22 | this._use(handler); 23 | } 24 | return this; 25 | } 26 | 27 | end (cb) { 28 | this.actMeta.action = cb; 29 | } 30 | 31 | dispatch (request, response, cb) { 32 | Util.serial(this.middleware, (item, next) => { 33 | item(request, response, next); 34 | }, cb); 35 | } 36 | 37 | get middleware () { 38 | return this.actMeta.middleware; 39 | } 40 | 41 | get schema () { 42 | return this.actMeta.schema; 43 | } 44 | 45 | get pattern () { 46 | return this.actMeta.pattern; 47 | } 48 | 49 | set action (action) { 50 | if (Util.isGeneratorFunction(action)) { 51 | this.actMeta.action = Co.wrap(action); 52 | this.isPromisable = true; 53 | } else if (Util.isAsyncFunction(action)) { 54 | this.actMeta.action = action; 55 | this.isPromisable = true; 56 | } else { 57 | this.actMeta.action = action; 58 | this.isPromisable = false; 59 | } 60 | } 61 | 62 | get action () { 63 | return this.actMeta.action; 64 | } 65 | 66 | get plugin () { 67 | return this.actMeta.plugin; 68 | } 69 | 70 | } 71 | 72 | module.exports = Add; 73 | -------------------------------------------------------------------------------- /src/before-exit.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Errors = require('./errors'); 3 | 4 | class BeforeExit { 5 | 6 | constructor () { 7 | this.actions = []; 8 | this.signals = ['SIGINT', 'SIGTERM']; 9 | } 10 | 11 | addAction (fn) { 12 | if (!_.isFunction(fn)) { 13 | throw new Errors.MostlyError('Expected a function but got a ' + typeof fn); 14 | } 15 | this.actions.push(fn); 16 | } 17 | 18 | doActions (signal) { 19 | try { 20 | Promise.all(this.actions.map(action => action(signal))) 21 | .then(() => { 22 | process.exit(0); 23 | }) 24 | .catch(() => { 25 | process.exit(1); 26 | }); 27 | } catch (err) { 28 | process.exit(1); 29 | } 30 | } 31 | 32 | init () { 33 | this.signals.forEach((signal) => { 34 | process.on(signal, () => { 35 | this.doActions(signal); 36 | }); 37 | }); 38 | 39 | // PM2 Cluster shutdown message. Caught to support async handlers with pm2, needed because 40 | // explicitly calling process.exit() doesn't trigger the beforeExit event, and the exit 41 | // event cannot support async handlers, since the event loop is never called after it. 42 | process.on('message', (msg) => { 43 | if (msg === 'shutdown') { 44 | this.doActions('shutdown'); 45 | } 46 | }); 47 | } 48 | } 49 | 50 | 51 | 52 | module.exports = BeforeExit; -------------------------------------------------------------------------------- /src/check-plugin.js: -------------------------------------------------------------------------------- 1 | const semver = require('semver'); 2 | 3 | // Check the bare-minimum version of mostly-node 4 | // Provide consistent interface to register plugins even when the api is changed 5 | function checkPlugin (fn, version) { 6 | if (typeof fn !== 'function') { 7 | throw new TypeError(`mostly-plugin expects a function, instead got a '${typeof fn}'`); 8 | } 9 | 10 | if (version) { 11 | if (typeof version !== 'string') { 12 | throw new TypeError(`mostly-plugin expects a version string as second parameter, instead got '${typeof version}'`); 13 | } 14 | 15 | const mostlyVersion = require('mostly-node/package.json').version; 16 | if (!semver.satisfies(mostlyVersion, version)) { 17 | throw new Error(`mostly-plugin - expected '${version}' mostly-node version, '${mostlyVersion}' is installed`); 18 | } 19 | } 20 | 21 | return fn; 22 | } 23 | 24 | module.exports = checkPlugin; -------------------------------------------------------------------------------- /src/circuit-breaker.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | 3 | /** 4 | * Based on https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker 5 | * 6 | * A circuit breaker acts as a proxy as a state machine for operations that might fail. 7 | * 8 | * @class CircuitBreaker 9 | * @extends {EventEmitter} 10 | */ 11 | class CircuitBreaker extends EventEmitter { 12 | constructor (options) { 13 | super(); 14 | 15 | // states 16 | this.CIRCUIT_CLOSE = 'close'; 17 | this.CIRCUIT_HALF_OPEN = 'half_open'; 18 | this.CIRCUIT_OPEN = 'open'; 19 | 20 | // intial state 21 | this._state = this.CIRCUIT_CLOSE; 22 | // current fauilures 23 | this._failureCount = 0; 24 | // successes count 25 | this._successesCount = 0; 26 | // max failure threshold 27 | this._maxFailures = options.maxFailures; 28 | // min successes to close the circuit breaker 29 | this._minSuccesses = options.minSuccesses; 30 | // the timeout when the circuit breaker in in half open state 31 | this._halfOpenTime = options.halfOpenTime; 32 | // interval when the circuit breaker will reset 33 | this._resetIntervalTime = options.resetIntervalTime; 34 | // half open timer 35 | this._halfOpenTimer = null; 36 | } 37 | 38 | get state () { 39 | return this._state; 40 | } 41 | 42 | /** 43 | * The failure counter used by the Closed state is time based. It's automatically reset at periodic intervals. 44 | * This helps to prevent the circuit breaker from entering the Open state if it experiences occasional failures 45 | */ 46 | startResetInterval () { 47 | this._resetInterval = setInterval(() => { 48 | this._state = this.CIRCUIT_CLOSE; 49 | this._failureCount = 0; 50 | }, this._resetIntervalTime); 51 | } 52 | 53 | clearHalfOpenTimer () { 54 | if (this._halfOpenTimer) { 55 | clearTimeout(this._halfOpenTimer); 56 | this._halfOpenTimer = null; 57 | } 58 | } 59 | 60 | startHalfOpenTimer () { 61 | // avoid starting new timer when existing already ticks 62 | if (!this._halfOpenTimer) { 63 | this._halfOpenTimer = setTimeout(() => { 64 | this._successesCount = 0; 65 | this._state = this.CIRCUIT_HALF_OPEN; 66 | this._halfOpenTimer = null; 67 | this.emit('circuitHalfOpen', this.toJSON()); 68 | }, this._halfOpenTime); 69 | // unref from event loop 70 | this._halfOpenTimer.unref(); 71 | } 72 | } 73 | 74 | clearResetInterval () { 75 | if (this._resetInterval) { 76 | clearInterval(this._resetInterval); 77 | } 78 | } 79 | 80 | toJSON () { 81 | return { 82 | state: this._state, 83 | failures: this._failureCount, 84 | successes: this._successesCount 85 | }; 86 | } 87 | 88 | available () { 89 | return this._state !== this.CIRCUIT_OPEN; 90 | } 91 | 92 | success () { 93 | this.record(true); 94 | } 95 | 96 | failure () { 97 | this.record(false); 98 | } 99 | 100 | record (success) { 101 | if (this._state === this.CIRCUIT_HALF_OPEN) { 102 | // The counter used by the Half-Open state records the number of successful attempts to invoke the operation. 103 | // The circuit breaker reverts to the Closed state after a specified number of consecutive operation invocations have been successful. 104 | if (success === true) { 105 | // request was successfully so increment it 106 | if (this._successesCount >= this._minSuccesses) { 107 | this._state = this.CIRCUIT_CLOSE; 108 | this.emit('circuitClose', this.toJSON()); 109 | // reset failure count and clear half-open timeout 110 | this._failureCount = 0; 111 | this.clearHalfOpenTimer(); 112 | } 113 | // request was successfully we increment it 114 | this._successesCount += 1; 115 | // this.emit('success', this.toJSON()); 116 | } else if (success === false) { 117 | // If any invocation fails, the circuit breaker enters the Open state immediately and 118 | // the success counter will be reset the next time it enters the Half-Open state. 119 | this._state = this.CIRCUIT_OPEN; 120 | this.emit('circuitOpen', this.toJSON()); 121 | this.clearHalfOpenTimer(); 122 | } 123 | } else if (this._state === this.CIRCUIT_OPEN) { 124 | this._failureCount = 0; 125 | // At this point the proxy starts a timeout timer 126 | // and when this timer expires the proxy is placed into the Half-Open state. 127 | this.startHalfOpenTimer(); 128 | } else if (this._state === this.CIRCUIT_CLOSE) { 129 | if (success === false) { 130 | // when request fails we increment the failureCount 131 | this._failureCount += 1; 132 | } 133 | 134 | // when we reach maximum failure threshold we open the circuit breaker and start the reset timer 135 | if (this._failureCount >= this._maxFailures) { 136 | this._state = this.CIRCUIT_OPEN; 137 | this.emit('circuitOpen', this.toJSON()); 138 | this.clearResetInterval(); 139 | this.startResetInterval(); 140 | } 141 | } 142 | } 143 | } 144 | 145 | module.exports = CircuitBreaker; -------------------------------------------------------------------------------- /src/client-request.js: -------------------------------------------------------------------------------- 1 | class ClientRequest { 2 | 3 | constructor () { 4 | this._request = {}; 5 | } 6 | 7 | get payload () { 8 | return this._request.value; 9 | } 10 | 11 | get error () { 12 | return this._request.error; 13 | } 14 | 15 | set payload (value) { 16 | this._request.value = value; 17 | } 18 | 19 | set error (error) { 20 | this._request.error = error; 21 | } 22 | 23 | } 24 | 25 | module.exports = ClientRequest; -------------------------------------------------------------------------------- /src/client-response.js: -------------------------------------------------------------------------------- 1 | class ClientResponse { 2 | 3 | constructor () { 4 | this._response = {}; 5 | } 6 | 7 | get payload () { 8 | return this._response.value; 9 | } 10 | 11 | set payload (value) { 12 | this._response.value = value; 13 | } 14 | 15 | set error (error) { 16 | this._response.error = error; 17 | } 18 | 19 | get error () { 20 | return this._response.error; 21 | } 22 | 23 | } 24 | 25 | module.exports = ClientResponse; -------------------------------------------------------------------------------- /src/codec-pipeline.js: -------------------------------------------------------------------------------- 1 | class CodecPipeline { 2 | constructor () { 3 | this._stack = []; 4 | return this; 5 | } 6 | 7 | add (step) { 8 | this._stack.push(step); 9 | return this; 10 | } 11 | 12 | /** 13 | * Reset the stack and add optionally an element 14 | */ 15 | reset (step) { 16 | this._stack = step? [step] : []; 17 | return this; 18 | } 19 | 20 | /** 21 | * Add the element at the beginning of the stack 22 | */ 23 | first (step) { 24 | this._stack.unshift(step); 25 | return this; 26 | } 27 | 28 | /** 29 | * Accumulate value 30 | */ 31 | run (msg, ctx) { 32 | let firstError = null; 33 | 34 | const value = this._stack.reduce((data, item, index) => { 35 | const result = item.call(ctx, data); 36 | if (!firstError && result.error) { 37 | firstError = result.error; 38 | } 39 | return result.value; 40 | }, msg); 41 | 42 | return { value, error: firstError }; 43 | } 44 | } 45 | 46 | module.exports = CodecPipeline; -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | // Errors messages 2 | module.exports = { 3 | // General 4 | NATS_QUEUEGROUP_PREFIX: 'queue', 5 | // NATS error codes 6 | NATS_CONN_ERROR_CODES: ['CONN_ERR', 'SECURE_CONN_REQ_MSG', 'NON_SECURE_CONN_REQ_MSG', 'CLIENT_CERT_REQ_MSG'], 7 | // NATS erros 8 | NATS_TRANSPORT_ERROR: 'Could not connect to NATS!', 9 | NATS_TRANSPORT_CLOSED: 'NATS connection closed!', 10 | NATS_TRANSPORT_CONNECTED: 'NATS connected!', 11 | NATS_PERMISSION_ERROR: 'NATS permission error', 12 | NATS_TRANSPORT_RECONNECTING: 'NATS reconnecting ...', 13 | NATS_TRANSPORT_RECONNECTED: 'NATS reconnected!', 14 | NATS_TRANSPORT_DISCONNECTED: 'NATS disconnected!', 15 | // Request types 16 | REQUEST_TYPE_PUBSUB: 'pubsub', 17 | REQUEST_TYPE_REQUEST: 'request', 18 | // Application errors 19 | JSON_PARSE_ERROR: 'Invalid JSON payload', 20 | TOPIC_SID_REQUIRED_FOR_DELETION: 'Topic or sid is required for deletion', 21 | ACT_TIMEOUT_ERROR: 'Timeout', 22 | NO_TOPIC_TO_SUBSCRIBE: 'No topic to subscribe', 23 | NO_TOPIC_TO_REQUEST: 'No topic to request', 24 | PATTERN_ALREADY_IN_USE: 'Pattern is already in use', 25 | INVALID_ERROR_OBJECT: 'No native Error object passed', 26 | PATTERN_NOT_FOUND: 'No handler found for this pattern', 27 | IMPLEMENTATION_ERROR: 'Bad implementation', 28 | PAYLOAD_PARSING_ERROR: 'Invalid payload', 29 | ADD_MIDDLEWARE_ERROR: 'Middleware error', 30 | PLUGIN_ALREADY_REGISTERED: 'Plugin was already registered', 31 | PLUGIN_ADDED: 'PLUGIN - ADDED!', 32 | PAYLOAD_VALIDATION_ERROR: 'Invalid payload', 33 | ADD_ADDED: 'ADD - ADDED', 34 | BUSINESS_ERROR: 'Business error', 35 | FATAL_ERROR: 'Fatal error', 36 | EXTENSION_ERROR: 'Extension error', 37 | PUB_CALLBACK_REDUNDANT: 'Specify a callback as publisher is redundant', 38 | INVALID_EXTENSION_TYPE: 'Invalid extension type', 39 | PLUGIN_NAME_REQUIRED: 'Plugin name is required', 40 | PLUGIN_REGISTRATION_ERROR: 'Error during plugin registration', 41 | DECORATION_ALREADY_DEFINED: 'Server decoration already defined', 42 | OVERRIDE_BUILTIN_METHOD_NOT_ALLOWED: 'Cannot override the built-in server interface method', 43 | GRACEFULLY_SHUTDOWN: 'Gracefully shutdown', 44 | PLUGIN_TIMEOUT_ERROR: 'Plugin callback was not called', 45 | ACT_PATTERN_REQUIRED: 'Pattern is required to start an act call', 46 | ADD_PATTERN_REQUIRED: 'Pattern is required to define an add' 47 | }; 48 | -------------------------------------------------------------------------------- /src/decoder.js: -------------------------------------------------------------------------------- 1 | function Parse (data) { 2 | if (!(this instanceof Parse)) { 3 | return new Parse(data); 4 | } 5 | 6 | this.error = null; 7 | this.value = null; 8 | 9 | try { 10 | this.value = JSON.parse(data); 11 | } catch (error) { 12 | this.error = error; 13 | } 14 | } 15 | 16 | class Decoder { 17 | 18 | static decode (msg) { 19 | return Parse(msg); 20 | } 21 | } 22 | 23 | module.exports = Decoder; -------------------------------------------------------------------------------- /src/encoder.js: -------------------------------------------------------------------------------- 1 | const SafeStringify = require('fast-safe-stringify'); 2 | 3 | class Encoder { 4 | 5 | static encode (msg) { 6 | try { 7 | return { 8 | value: SafeStringify(msg) 9 | }; 10 | } catch (error) { 11 | return { error }; 12 | } 13 | } 14 | } 15 | 16 | module.exports = Encoder; -------------------------------------------------------------------------------- /src/errors.js: -------------------------------------------------------------------------------- 1 | const SuperError = require('super-error'); 2 | 3 | const MostlyError = SuperError.subclass('MostlyError'); 4 | const ParseError = MostlyError.subclass('ParseError'); 5 | const TimeoutError = MostlyError.subclass('TimeoutError'); 6 | const ImplementationError = MostlyError.subclass('ImplementationError'); 7 | const BusinessError = MostlyError.subclass('BusinessError'); 8 | const FeathersError = MostlyError.subclass('FeathersError'); 9 | const FatalError = MostlyError.subclass('FatalError'); 10 | const PatternNotFound = MostlyError.subclass('PatternNotFound'); 11 | const MaxRecursionError = MostlyError.subclass('MaxRecursionError'); 12 | const PayloadValidationError = MostlyError.subclass('PayloadValidationError'); 13 | const CircuitBreakerError = MostlyError.subclass('CircuitBreakerError'); 14 | const PluginTimeoutError = MostlyError.subclass('PluginTimeoutError'); 15 | 16 | module.exports = { 17 | MostlyError, 18 | MaxRecursionError, 19 | ParseError, 20 | TimeoutError, 21 | ImplementationError, 22 | BusinessError, 23 | FeathersError, 24 | FatalError, 25 | PatternNotFound, 26 | PayloadValidationError, 27 | CircuitBreakerError, 28 | PluginTimeoutError 29 | }; 30 | -------------------------------------------------------------------------------- /src/extension.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Reply = require('./reply'); 3 | const Util = require('./util'); 4 | 5 | class Extension { 6 | 7 | constructor (type) { 8 | this._stack = []; 9 | this._type = type; 10 | } 11 | 12 | _add (handler) { 13 | this._stack.push(Util.toPromiseFact(handler)); 14 | } 15 | 16 | add (handler) { 17 | if (_.isArray(handler)) { 18 | handler.forEach(h => this._add(h)); 19 | } else { 20 | this._add(handler); 21 | } 22 | } 23 | 24 | addRange (handlers) { 25 | this._stack = this._stack.concat(handlers); 26 | } 27 | 28 | /* 29 | * Executes the stack of callbacks and set the correct 30 | * response and request context 31 | */ 32 | dispatch (ctx, cb) { 33 | const each = (item, next, prevValue, i) => { 34 | if (ctx._isServer) { 35 | const response = ctx._response; 36 | const request = ctx._request; 37 | const reply = new Reply(request, response, next); 38 | 39 | item(ctx, request, reply, next); 40 | } else { 41 | item(ctx, next); 42 | } 43 | }; 44 | 45 | Util.serialWithCancellation(this._stack, each, cb); 46 | } 47 | 48 | // unused function 49 | static parallel (array, method, callback) { 50 | if (!array.length) { 51 | callback(); 52 | } else { 53 | let count = 0; 54 | let abort = false; 55 | let errored = false; 56 | 57 | const done = function (err, value, cancel) { 58 | if (!errored && !abort) { 59 | if (err) { 60 | errored = true; 61 | callback(err); 62 | } else if (value && cancel) { 63 | abort = true; 64 | callback(null, value); 65 | } else { 66 | count = count + 1; 67 | if (count === array.length) { 68 | callback(null, value); 69 | } 70 | } 71 | } 72 | }; 73 | 74 | for (let i = 0; i < array.length; ++i) { 75 | method(array[i], done, i); 76 | } 77 | } 78 | } 79 | 80 | // unused function 81 | static serial (array, method, callback) { 82 | if (!array.length) { 83 | callback(); 84 | } else { 85 | let i = 0; 86 | 87 | const iterate = function iterate (prevValue) { 88 | const done = function (err, value, abort) { 89 | if (err) { 90 | callback(err); 91 | } else if (value && abort) { 92 | callback(null, value); 93 | } else { 94 | i = i + 1; 95 | 96 | if (i < array.length) { 97 | iterate(value); 98 | } else { 99 | callback(null, value); 100 | } 101 | } 102 | }; 103 | 104 | method(array[i], done, prevValue, i); 105 | }; 106 | 107 | iterate(); 108 | } 109 | } 110 | } 111 | 112 | module.exports = Extension; -------------------------------------------------------------------------------- /src/extensions.js: -------------------------------------------------------------------------------- 1 | const Constants = require('./constants'); 2 | const Util = require('./util'); 3 | const Errors = require('./errors'); 4 | const CircuitBreaker = require('./circuit-breaker'); 5 | 6 | function onClientPreRequest (ctx, next) { 7 | let pattern = ctx._pattern; 8 | 9 | let prevCtx = ctx._prevContext; 10 | let cleanPattern = ctx._cleanPattern; 11 | let currentTime = Util.nowHrTime(); 12 | 13 | // shared context 14 | ctx.context$ = pattern.context$ || prevCtx.context$; 15 | 16 | // set metadata by passed pattern or current message context 17 | ctx.meta$ = Object.assign(pattern.meta$ || {}, ctx.meta$); 18 | // is only passed by msg 19 | ctx.delegate$ = pattern.delegate$ || {}; 20 | 21 | // tracing 22 | ctx.trace$ = pattern.trace$ || {}; 23 | ctx.trace$.parentSpanId = ctx.trace$.spanId || prevCtx.trace$.spanId; 24 | ctx.trace$.traceId = ctx.trace$.traceId || prevCtx.trace$.traceId || Util.randomId(); 25 | ctx.trace$.spanId = Util.randomId(); 26 | ctx.trace$.timestamp = currentTime; 27 | ctx.trace$.service = pattern.topic; 28 | ctx.trace$.method = Util.pattern(pattern); 29 | 30 | // detect recursion 31 | if (ctx._config.maxRecursion > 1) { 32 | const callSignature = `${ctx.trace$.traceId}:${ctx.trace$.method}`; 33 | if (ctx.meta$ && ctx.meta$.referrers) { 34 | var count = ctx.meta$.referrers[callSignature]; 35 | count += 1; 36 | ctx.meta$.referrers[callSignature] = count; 37 | if (count > ctx._config.maxRecursion) { 38 | ctx.meta$.referrers = null; 39 | return next(new Errors.MaxRecursionError({ count: --count })); 40 | } 41 | } else { 42 | ctx.meta$.referrers = {}; 43 | ctx.meta$.referrers[callSignature] = 1; 44 | } 45 | } 46 | 47 | // request 48 | let request = { 49 | id: pattern.requestId$ || Util.randomId(), 50 | parentId: ctx.request$.id || pattern.requestParentId$, 51 | type: pattern.pubsub$ === true? Constants.REQUEST_TYPE_PUBSUB : Constants.REQUEST_TYPE_REQUEST 52 | }; 53 | 54 | ctx.emit('clientPreRequest', ctx); 55 | 56 | // build msg 57 | let message = { 58 | pattern: cleanPattern, 59 | meta: ctx.meta$, 60 | delegate: ctx.delegate$, 61 | trace: ctx.trace$, 62 | request: request 63 | }; 64 | 65 | ctx._message = message; 66 | 67 | ctx.log.debug({ 68 | outbound: ctx 69 | }); 70 | 71 | next(); 72 | } 73 | 74 | // circuit breaker on client side 75 | function onClientPreRequestCircuitBreaker (ctx, next) { 76 | if (ctx._config.circuitBreaker.enabled) { 77 | // any pattern represent an own circuit breaker 78 | const circuitBreaker = ctx._circuitBreakerMap.get(ctx.trace$.method); 79 | if (!circuitBreaker) { 80 | const cb = new CircuitBreaker(ctx._config.circuitBreaker); 81 | ctx._circuitBreakerMap.set(ctx.trace$.method, cb); 82 | } else { 83 | if (!circuitBreaker.available()) { 84 | // trigger half-open timer 85 | circuitBreaker.record(); 86 | return next(new Errors.CircuitBreakerError( 87 | `Circuit breaker is ${circuitBreaker.state}`, 88 | { state: circuitBreaker.state, method: ctx.trace$.method, service: ctx.trace$.service })); 89 | } 90 | } 91 | 92 | next(); 93 | } else { 94 | next(); 95 | } 96 | } 97 | 98 | function onClientPostRequest (ctx, next) { 99 | let pattern = ctx._pattern; 100 | let msg = ctx._response.payload; 101 | 102 | // pass to act context 103 | if (msg) { 104 | ctx.request$ = msg.request || {}; 105 | ctx.trace$ = msg.trace || {}; 106 | ctx.meta$ = msg.meta || {}; 107 | } 108 | 109 | // calculate request duration 110 | let diff = Util.nowHrTime() - ctx.trace$.timestamp; 111 | ctx.trace$.duration = diff; 112 | 113 | ctx.request$.service = pattern.topic; 114 | ctx.request$.method = ctx.trace$.method; 115 | 116 | ctx.log.debug({ 117 | inbound: ctx 118 | }); 119 | 120 | ctx.emit('clientPostRequest', ctx); 121 | 122 | next(); 123 | } 124 | 125 | function onServerPreRequest (ctx, req, res, next) { 126 | let m = ctx._decoderPipeline.run(ctx._request.payload, ctx); 127 | 128 | if (m.error) { 129 | return res.send(m.error); 130 | } 131 | 132 | let msg = m.value; 133 | 134 | if (msg) { 135 | ctx.meta$ = msg.meta || {}; 136 | ctx.trace$ = msg.trace || {}; 137 | ctx.delegate$ = msg.delegate || {}; 138 | ctx.request$ = msg.request || {}; 139 | ctx.auth$ = {}; 140 | } 141 | 142 | ctx._request.payload = m.value; 143 | ctx._request.error = m.error; 144 | 145 | // incoming pattern 146 | ctx._pattern = ctx._request.payload.pattern; 147 | 148 | // find matched route 149 | ctx._actMeta = ctx._router.lookup(ctx._pattern); 150 | 151 | ctx.emit('serverPreRequest', ctx); 152 | 153 | next(); 154 | } 155 | 156 | function onServerPreRequestLoadTest (ctx, req, res, next) { 157 | if (ctx._config.load.checkPolicy) { 158 | const error = ctx._loadPolicy.check(); 159 | if (error) { 160 | ctx._shouldCrash = ctx._config.load.shouldCrash; 161 | return next(new Errors.ProcessLoadError(error.message, error.data)); 162 | } 163 | } 164 | 165 | next(); 166 | } 167 | 168 | function onServerPreHandler (ctx, req, res, next) { 169 | ctx.emit('serverPreHandler', ctx); 170 | 171 | next(); 172 | } 173 | 174 | function onServerPreResponse (ctx, req, res, next) { 175 | ctx.emit('serverPreResponse', ctx); 176 | 177 | next(); 178 | } 179 | 180 | module.exports = { 181 | onClientPreRequest: [onClientPreRequest, onClientPreRequestCircuitBreaker], 182 | onClientPostRequest: [onClientPostRequest], 183 | onServerPreRequest: [onServerPreRequest, onServerPreRequestLoadTest], 184 | onServerPreHandler: [onServerPreHandler], 185 | onServerPreResponse: [onServerPreResponse] 186 | }; 187 | -------------------------------------------------------------------------------- /src/handlers.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const makeDebug = require('debug'); 3 | const Errio = require('errio'); 4 | const SuperError = require('super-error'); 5 | 6 | const Errors = require('./errors'); 7 | const Constants = require('./constants'); 8 | 9 | const debug = makeDebug('mostly:core:handlers'); 10 | 11 | /** 12 | * Is called after the client has received and decoded the request 13 | */ 14 | function onClientPostRequestHandler (ctx, err) { 15 | // extension error 16 | if (err) { 17 | let error = null; 18 | if (err instanceof SuperError) { 19 | error = err.rootCause || err.cause || err; 20 | } else { 21 | error = err; 22 | } 23 | const internalError = new Errors.MostlyError(Constants.EXTENSION_ERROR, ctx.errorDetails).causedBy(err); 24 | ctx.log.error(internalError); 25 | 26 | ctx.emit('clientResponseError', error); 27 | 28 | ctx._execute(error); 29 | return; 30 | } 31 | 32 | if (ctx._response.payload.error) { 33 | debug('act:response.payload.error', ctx._response.payload.error); 34 | let error = Errio.fromObject(ctx._response.payload.error); 35 | 36 | const internalError = new Errors.BusinessError(Constants.BUSINESS_ERROR, ctx.errorDetails).causedBy(error); 37 | ctx.log.error(internalError); 38 | 39 | ctx.emit('clientResponseError', error); 40 | 41 | ctx._execute(error); 42 | return; 43 | } 44 | 45 | ctx._execute(null, ctx._response.payload.result); 46 | } 47 | 48 | /** 49 | * Is called after the client request timeout 50 | */ 51 | function onClientTimeoutPostRequestHandler (ctx, err) { 52 | if (err) { 53 | let error = null; 54 | if (err instanceof SuperError) { 55 | error = err.rootCause || err.cause || err; 56 | } else { 57 | error = err; 58 | } 59 | 60 | let internalError = new Errors.MostlyError(Constants.EXTENSION_ERROR).causedBy(err); 61 | ctx.log.error(internalError); 62 | 63 | ctx._response.error = error; 64 | ctx.emit('clientResponseError', error); 65 | } 66 | 67 | try { 68 | ctx._execute(ctx._response.error); 69 | } catch(err) { 70 | let error = null; 71 | if (err instanceof SuperError) { 72 | error = err.rootCause || err.cause || err; 73 | } else { 74 | error = err; 75 | } 76 | 77 | let internalError = new Errors.FatalError(Constants.FATAL_ERROR, ctx.errorDetails).causedBy(err); 78 | ctx.log.fatal(internalError); 79 | 80 | ctx.emit('clientResponseError', error); 81 | 82 | // let it crash 83 | if (ctx._config.crashOnFatal) { 84 | ctx.fatal(); 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * Is called before the client has send the request to NATS 91 | */ 92 | function onPreRequestHandler (ctx, err) { 93 | let m = ctx._encoderPipeline.run(ctx._message, ctx); 94 | 95 | // encoding issue 96 | if (m.error) { 97 | let error = new Errors.ParseError(Constants.PAYLOAD_PARSING_ERROR).causedBy(m.error); 98 | ctx.log.error(error); 99 | ctx.emit('clientResponseError', error); 100 | 101 | ctx._execute(error); 102 | return; 103 | } 104 | 105 | if (err) { 106 | let error = null; 107 | if (err instanceof SuperError) { 108 | error = err.rootCause || err.cause || err; 109 | } else { 110 | error = err; 111 | } 112 | 113 | const internalError = new Errors.MostlyError(Constants.EXTENSION_ERROR).causedBy(err); 114 | ctx.log.error(internalError); 115 | 116 | ctx.emit('clientResponseError', error); 117 | 118 | ctx._execute(error); 119 | return; 120 | } 121 | 122 | ctx._request.payload = m.value; 123 | ctx._request.error = m.error; 124 | 125 | // use simple publish mechanism instead of request/reply 126 | if (ctx._pattern.pubsub$ === true) { 127 | if (ctx._actCallback) { 128 | ctx.log.info(Constants.PUB_CALLBACK_REDUNDANT); 129 | } 130 | 131 | ctx._transport.send(ctx._pattern.topic, ctx._request.payload); 132 | } else { 133 | const optOptions = {}; 134 | // limit on the number of responses the requestor may receive 135 | if (ctx._pattern.maxMessages$ > 0) { 136 | optOptions.max = ctx._pattern.maxMessages$; 137 | } else if (ctx._pattern.maxMessages$ !== -1) { 138 | optOptions.max = 1; 139 | } // else unlimited messages 140 | 141 | // send request 142 | ctx._sid = ctx._transport.sendRequest(ctx._pattern.topic, 143 | ctx._request.payload, optOptions, ctx._sendRequestHandler.bind(ctx)); 144 | 145 | // handle timeout 146 | ctx.handleTimeout(); 147 | } 148 | } 149 | 150 | /** 151 | * Is called before the server action is executed 152 | */ 153 | function onServerPreHandler (ctx, err, value) { 154 | if (err) { 155 | if (err instanceof SuperError) { 156 | ctx._response.error = err.rootCause || err.cause || err; 157 | } else { 158 | ctx._response.error = err; 159 | } 160 | 161 | const internalError = new Errors.MostlyError( 162 | Constants.EXTENSION_ERROR, ctx.errorDetails).causedBy(err); 163 | ctx.log.error(internalError); 164 | 165 | return ctx.finish(); 166 | } 167 | 168 | // reply value from extension 169 | if (value) { 170 | ctx._response.payload = value; 171 | return ctx.finish(); 172 | } 173 | 174 | try { 175 | let action = ctx._actMeta.action.bind(ctx); 176 | 177 | // execute add middlewares 178 | ctx._actMeta.dispatch(ctx._request, ctx._response, (err) => { 179 | // middleware error 180 | if (err) { 181 | if (err instanceof SuperError) { 182 | ctx._response.error = err.rootCause || err.cause || err; 183 | } else { 184 | ctx._response.error = err; 185 | } 186 | 187 | let internalError = new Errors.MostlyError( 188 | Constants.ADD_MIDDLEWARE_ERROR, ctx.errorDetails).causedBy(err); 189 | ctx.log.error(internalError); 190 | 191 | return ctx.finish(); 192 | } 193 | 194 | // if request type is 'pubsub' we dont have to reply back 195 | if (ctx._request.payload.request.type === Constants.REQUEST_TYPE_PUBSUB) { 196 | action(ctx._request.payload.pattern); 197 | return ctx.finish(); 198 | } 199 | 200 | // execute RPC action 201 | if (ctx._actMeta.isPromisable) { 202 | action(ctx._request.payload.pattern) 203 | .then(x => ctx._actionHandler(null, x)) 204 | .catch(e => ctx._actionHandler(e)); 205 | } else { 206 | action(ctx._request.payload.pattern, ctx._actionHandler.bind(ctx)); 207 | } 208 | }); 209 | } catch (err) { 210 | if (err instanceof SuperError) { 211 | ctx._response.error = err.rootCause || err.cause || err; 212 | } else { 213 | ctx._response.error = err; 214 | } 215 | 216 | // service should exit 217 | ctx._shouldCrash = true; 218 | 219 | ctx.finish(); 220 | } 221 | } 222 | 223 | /** 224 | * Is called before the server has received the request 225 | */ 226 | function onServerPreRequestHandler (ctx, err, value) { 227 | if (err) { 228 | if (err instanceof SuperError) { 229 | ctx._response.error = err.rootCause || err.cause || err; 230 | } else { 231 | ctx._response.error = err; 232 | } 233 | 234 | return ctx.finish(); 235 | } 236 | 237 | // reply value from extension 238 | if (value) { 239 | ctx._response.payload = value; 240 | return ctx.finish(); 241 | } 242 | 243 | // check if a handler is registered with this pattern 244 | if (ctx._actMeta) { 245 | ctx._extensions.onServerPreHandler.dispatch(ctx, (err, val) => { 246 | return onServerPreHandler(ctx, err, val); 247 | }); 248 | } else { 249 | const internalError = new Errors.PatternNotFound(Constants.PATTERN_NOT_FOUND, ctx.errorDetails); 250 | ctx.log.error(internalError); 251 | ctx._response.error = internalError; 252 | 253 | // send error back to callee 254 | ctx.finish(); 255 | } 256 | } 257 | 258 | /** 259 | * Is called before the server has replied and build the message 260 | */ 261 | function onServerPreResponseHandler (ctx, err, value) { 262 | // check if an error was already wrapped 263 | if (ctx._response.error) { 264 | ctx.emit('serverResponseError', ctx._response.error); 265 | ctx.log.error(ctx._response.error); 266 | } else if (err) { // check for an extension error 267 | if (err instanceof SuperError) { 268 | ctx._response.error = err.rootCause || err.cause || err; 269 | } else { 270 | ctx._response.error = err; 271 | } 272 | const internalError = new Errors.MostlyError( 273 | Constants.EXTENSION_ERROR, ctx.errorDetails).causedBy(err); 274 | ctx.log.error(internalError); 275 | 276 | ctx.emit('serverResponseError', ctx._response.error); 277 | } 278 | 279 | // reply value from extension 280 | if (value) { 281 | ctx._response.payload = value; 282 | } 283 | 284 | // create message payload 285 | ctx._buildMessage(); 286 | 287 | // indicates that an error occurs and that the program should exit 288 | if (ctx._shouldCrash) { 289 | // only when we have an inbox othwerwise exit the service immediately 290 | if (ctx._replyTo) { 291 | // send error back to callee 292 | return ctx._transport.send(ctx._replyTo, ctx._message, () => { 293 | // let it crash 294 | if (ctx._config.crashOnFatal) { 295 | ctx.fatal(); 296 | } 297 | }); 298 | } else if (ctx._config.crashOnFatal) { 299 | return ctx.fatal(); 300 | } 301 | } 302 | 303 | // reply only when we have an inbox 304 | if (ctx._replyTo) { 305 | return ctx._transport.send(ctx._replyTo, ctx._message); 306 | } 307 | } 308 | 309 | /** 310 | * Is called after all onClose extensions have been called 311 | */ 312 | function onClose (ctx, err, val, cb) { 313 | // no callback no queue processing 314 | if (!_.isFunction(cb)) { 315 | ctx._heavy.stop(); 316 | ctx._transport.close(); 317 | if (err) { 318 | ctx.log.fatal(err); 319 | ctx.emit('error', err); 320 | } 321 | return; 322 | } 323 | 324 | // unsubscribe all active subscriptions 325 | ctx.removeAll(); 326 | 327 | // wait until the client has flush all messages to nats 328 | ctx._transport.flush(() => { 329 | ctx._heavy.stop(); 330 | // close NATS 331 | ctx._transport.close(); 332 | 333 | if (err) { 334 | ctx.log.error(err); 335 | ctx.emit('error', err); 336 | if (_.isFunction(cb)) { 337 | cb(err); 338 | } 339 | } else { 340 | ctx.log.info(Constants.GRACEFULLY_SHUTDOWN); 341 | if (_.isFunction(cb)) { 342 | cb(null, val); 343 | } 344 | } 345 | }); 346 | } 347 | 348 | module.exports = { 349 | onClientPostRequestHandler, 350 | onClientTimeoutPostRequestHandler, 351 | onPreRequestHandler, 352 | onServerPreHandler, 353 | onServerPreRequestHandler, 354 | onServerPreResponseHandler, 355 | onClose 356 | }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | const Os = require('os'); 3 | const Bloomrun = require('bloomrun'); 4 | const Errio = require('errio'); 5 | const Heavy = require('heavy'); 6 | const _ = require('lodash'); 7 | const Pino = require('pino'); 8 | const TinySonic = require('tinysonic'); 9 | const SuperError = require('super-error'); 10 | const Co = require('co'); 11 | const makeDebug = require('debug'); 12 | 13 | const BeforeExit = require('./before-exit'); 14 | const Errors = require('./errors'); 15 | const Constants = require('./constants'); 16 | const Extension = require('./extension'); 17 | const Util = require('./util'); 18 | const NatsTransport = require('./transport'); 19 | const DefaultExtensions = require('./extensions'); 20 | const DefaultEncoder = require('./encoder'); 21 | const DefaultDecoder = require('./decoder'); 22 | const ServerResponse = require('./server-response'); 23 | const ServerRequest = require('./server-request'); 24 | const ClientRequest = require('./client-request'); 25 | const ClientResponse = require('./client-response'); 26 | const Serializers = require('./serializer'); 27 | const CodecPipeline = require('./codec-pipeline'); 28 | const Add = require('./add'); 29 | const Plugin = require('./plugin'); 30 | const Handlers = require('./handlers'); 31 | 32 | const debug = makeDebug('mostly:node'); 33 | 34 | const defaultConfig = { 35 | timeout: 2000, // max execution time of a request 36 | pluginTimeout: 3000,// max intialization time for a plugin 37 | tag: '', // The tag string of this mostly instance 38 | prettyLog: true, // enables pino pretty logger (Don't use it in production the output isn't JSON) 39 | name: `node-${Os.hostname()}-${Util.randomId()}`, // node name 40 | crashOnFatal: true, // Should gracefully exit the process at unhandled exceptions or fatal errors 41 | logLevel: 'silent', // 'fatal', 'error', 'warn', 'info', 'debug', 'trace'; also 'silent' 42 | childLogger: false, // create a child logger per section / plugin. Only possible with default logger Pino. 43 | maxRecursion: 0, // max recursive method calls 44 | errio: { 45 | recursive: true, // recursively serialize and deserialize nested errors 46 | inherited: true, // include inherited properties 47 | stack: true, // include stack property 48 | private: false, // include properties with leading or trailing underscores 49 | exclude: [], // property names to exclude (low priority) 50 | include: [] // property names to include (high priority) 51 | }, 52 | bloomrun: { 53 | indexing: 'inserting', // pattern indexing method "inserting" or "depth" 54 | lookupBeforeAdd: true // checks if the pattern is no duplicate based on to the indexing strategy 55 | }, 56 | load: { 57 | checkPolicy: true, // check on every request (server) if the load policy was observed 58 | shouldCrash: true, // should gracefully exit the process to recover from memory leaks or load, crashOnFatal must be enabled 59 | process: { 60 | sampleInterval: 0 // frequency of load sampling in milliseconds (zero is no sampling) 61 | }, 62 | policy: { 63 | maxHeapUsedBytes: 0, // reject requests when V8 heap is over size in bytes (zero is no max) 64 | maxRssBytes: 0, // reject requests when process RSS is over size in bytes (zero is no max) 65 | maxEventLoopDelay: 0 // milliseconds of delay after which requests are rejected (zero is no max) 66 | } 67 | }, 68 | circuitBreaker: { 69 | enabled: false, 70 | minSuccesses: 1, // minimum successes in the half-open state to change to close state 71 | halfOpenTime: 5 * 1000, // the duration when the server is ready to accept further calls after changing to open state 72 | resetIntervalTime: 15 * 1000, // frequency of reseting the circuit breaker to close state in milliseconds 73 | maxFailures: 3 // the threshold when the circuit breaker change to open state 74 | } 75 | }; 76 | 77 | class MostlyCore extends EventEmitter { 78 | 79 | constructor (transport, options) { 80 | super(); 81 | 82 | options = options || {}; 83 | if (options.name) options.name = options.name + '-' + Util.randomId(); 84 | this._config = Object.assign(defaultConfig, options); 85 | this._router = Bloomrun(this._config.bloomrun); 86 | this._heavy = new Heavy(this._config.load.process); 87 | this._transport = new NatsTransport({ 88 | transport 89 | }); 90 | this._topics = {}; 91 | this._exposition = {}; 92 | 93 | // special variables for the new execution context 94 | this.context$ = {}; 95 | this.meta$ = {}; 96 | this.delegate$ = {}; 97 | this.auth$ = {}; 98 | this.plugin$ = new Plugin({ 99 | options: {}, 100 | attributes: { 101 | name: 'core' 102 | } 103 | }); 104 | this.trace$ = {}; 105 | this.request$ = { 106 | parentId: '', 107 | type: Constants.REQUEST_TYPE_REQUEST, 108 | id: '' 109 | }; 110 | 111 | // client and server locales 112 | this._shouldCrash = false; 113 | this._topic = ''; 114 | this._replyTo = ''; 115 | this._request = null; 116 | this._response = null; 117 | this._pattern = null; 118 | this._actMeta = null; 119 | this._actCallback = null; 120 | this._execute = null; 121 | this._cleanPattern = ''; 122 | this._pluginRegistrations = []; 123 | this._decorations = {}; 124 | // create reference to root mostly instance 125 | this._root = this; 126 | 127 | // contains the list of all registered plugins 128 | // the core is also a plugin 129 | this._plugins = { 130 | core: this.plugin$ 131 | }; 132 | 133 | this._encoderPipeline = new CodecPipeline().add(DefaultEncoder.encode); 134 | this._decoderPipeline = new CodecPipeline().add(DefaultDecoder.decode); 135 | 136 | // define extension points 137 | this._extensions = { 138 | onClientPreRequest: new Extension('onClientPreRequest'), 139 | onClientPostRequest: new Extension('onClientPostRequest'), 140 | onServerPreHandler: new Extension('onServerPreHandler'), 141 | onServerPreRequest: new Extension('onServerPreRequest'), 142 | onServerPreResponse: new Extension('onServerPreResponse'), 143 | onClose: new Extension('onClose') 144 | }; 145 | 146 | // errio settings 147 | Errio.setDefaults(this._config.errio); 148 | 149 | // create load policy 150 | this._loadPolicy = this._heavy.policy(this._config.load.policy); 151 | 152 | // start tracking process stats 153 | this._heavy.start(); 154 | 155 | // contains the list of circuit breaker of all act calls 156 | this._circuitBreakerMap = new Map(); 157 | 158 | // will be executed before the client request is executed. 159 | this._extensions.onClientPreRequest.add(DefaultExtensions.onClientPreRequest); 160 | // will be executed after the client has received and decoded the request 161 | this._extensions.onClientPostRequest.add(DefaultExtensions.onClientPostRequest); 162 | // will be executed before the server has received the requests 163 | this._extensions.onServerPreRequest.add(DefaultExtensions.onServerPreRequest); 164 | // will be executed before the server action is executed 165 | this._extensions.onServerPreHandler.add(DefaultExtensions.onServerPreHandler); 166 | // will be executed before the server has replied the response and build the message 167 | this._extensions.onServerPreResponse.add(DefaultExtensions.onServerPreResponse); 168 | 169 | // use own logger 170 | if (this._config.logger) { 171 | this.log = this._config.logger; 172 | } else { 173 | if (this._config.prettyLog) { 174 | let pretty = Pino.pretty(); 175 | this.log = Pino({ 176 | name: this._config.name, 177 | safe: true, // avoid error caused by circular references 178 | level: this._config.logLevel, 179 | serializers: Serializers 180 | }); 181 | 182 | // Leads to too much listeners in tests 183 | if (this._config.logLevel !== 'silent') { 184 | pretty.pipe(process.stdout); 185 | } 186 | } else { 187 | this.log = Pino({ 188 | name: this._config.name, 189 | safe: true, // avoid error caused by circular references 190 | level: this._config.logLevel, 191 | serializers: Serializers 192 | }); 193 | } 194 | } 195 | 196 | this._beforeExit = new BeforeExit(); 197 | 198 | this._beforeExit.addAction((signal) => { 199 | this.log.fatal({ signal }, 'process exited'); 200 | this.emit('exit', { signal }); 201 | this.close(); 202 | }); 203 | 204 | this._beforeExit.addAction(() => { 205 | return new Promise((resolve, reject) => { 206 | this.close((err) => { 207 | if (err) return reject(err); 208 | resolve(); 209 | }); 210 | }); 211 | }); 212 | 213 | this._beforeExit.init(); 214 | } 215 | 216 | /** 217 | * Return the decoder pipeline 218 | */ 219 | get decoder () { 220 | return this._decoderPipeline; 221 | } 222 | 223 | /** 224 | * Return the encoder pipeline 225 | */ 226 | get encoder () { 227 | return this._encoderPipeline; 228 | } 229 | 230 | /** 231 | * Return all registered plugins 232 | */ 233 | get plugins () { 234 | return this._plugins; 235 | } 236 | 237 | /** 238 | * Return the bloomrun instance 239 | */ 240 | get router () { 241 | return this._router; 242 | } 243 | 244 | /** 245 | * Return the heavy instance 246 | */ 247 | get load () { 248 | return this._heavy.load; 249 | } 250 | 251 | /** 252 | * Return the shared object of all exposed data 253 | */ 254 | get exposition () { 255 | return this._exposition; 256 | } 257 | 258 | /** 259 | * Return the underlying NATS driver 260 | */ 261 | get transport () { 262 | return this._transport.driver; 263 | } 264 | 265 | /** 266 | * Return all registered topics 267 | */ 268 | get topics () { 269 | return this._topics; 270 | } 271 | 272 | get config () { 273 | return this._config; 274 | } 275 | 276 | get errorDetails () { 277 | if (this._isServer) { 278 | return { 279 | app: this._config.name, 280 | isServer: this._isServer, 281 | pattern: this.trace$.method 282 | }; 283 | } else { 284 | return { 285 | app: this._config.name, 286 | isServer: this._isServer, 287 | pattern: this.trace$.method 288 | }; 289 | } 290 | } 291 | 292 | /** 293 | * Return all mostly errors 294 | */ 295 | static get errors () { 296 | return Errors; 297 | } 298 | 299 | /** 300 | * Exposed data in context of the current plugin 301 | * It is accessible by this.expositions[][] 302 | */ 303 | expose (key, object) { 304 | let pluginName = this.plugin$.attributes.name; 305 | 306 | if (!this._exposition[pluginName]) { 307 | this._exposition[pluginName] = {}; 308 | this._exposition[pluginName][key] = object; 309 | } else { 310 | this._exposition[pluginName][key] = object; 311 | } 312 | } 313 | 314 | /** 315 | * Add an extension. Extensions are called in serie 316 | */ 317 | ext (type, handler) { 318 | if (!this._extensions[type]) { 319 | let error = new Errors.MostlyError(Constants.INVALID_EXTENSION_TYPE, { 320 | type 321 | }); 322 | this.log.error(error); 323 | this.emit('error', error); 324 | } 325 | 326 | this._extensions[type].add(handler); 327 | } 328 | 329 | /** 330 | * Use a plugin. 331 | */ 332 | use (params, options) { 333 | // use plugin infos from package.json 334 | if (_.isObject(params.attributes.pkg)) { 335 | params.attributes = params.attributes || {}; 336 | params.attributes = Object.assign(params.attributes, 337 | _.pick(params.attributes.pkg, ['name', 'description', 'version'])); 338 | } 339 | 340 | let pluginOptions = {}; 341 | 342 | // pass options as second argument during plugin registration 343 | if (_.isObject(options)) { 344 | pluginOptions = _.clone(params.options) || {}; 345 | pluginOptions = _.defaults(pluginOptions, options); 346 | } else if (params.options) { 347 | pluginOptions = _.clone(params.options); 348 | } 349 | 350 | // plugin name is required 351 | if (!params.attributes.name) { 352 | let error = new Errors.MostlyError(Constants.PLUGIN_NAME_REQUIRED); 353 | this.log.error(error); 354 | this.emit('error', error); 355 | return; 356 | } 357 | 358 | // create new execution context 359 | let ctx = this.createContext(); 360 | 361 | const plugin = new Plugin({ 362 | register: params.plugin.bind(ctx), 363 | attributes: params.attributes, 364 | parentPluginName: this.plugin$.attributes.name, 365 | options: pluginOptions 366 | }); 367 | ctx.plugin$ = plugin; 368 | 369 | if (ctx._config.childLogger) { 370 | ctx.log = this.log.child({ plugin: plugin.attributes.name }); 371 | } 372 | 373 | this._pluginRegistrations.push(plugin); 374 | 375 | this.log.info(params.attributes.name, Constants.PLUGIN_ADDED); 376 | this._plugins[params.attributes.name] = plugin; 377 | } 378 | 379 | /** 380 | * Change the current plugin configuration 381 | * e.g to set the payload validator 382 | */ 383 | setOption (key, value) { 384 | this.plugin$.options[key] = value; 385 | } 386 | 387 | /** 388 | * Change the base configuration. 389 | */ 390 | setConfig (key, value) { 391 | this._config[key] = value; 392 | } 393 | 394 | /** 395 | * Exit the process 396 | */ 397 | fatal () { 398 | this._beforeExit.doActions('fatal'); 399 | } 400 | 401 | /** 402 | * Create a custom super error object without to start mostly 403 | */ 404 | static createError (name) { 405 | return SuperError.subclass(name); 406 | } 407 | 408 | /** 409 | * Create a custom super error object in a running mostly instance 410 | */ 411 | createError (name) { 412 | return SuperError.subclass(name); 413 | } 414 | 415 | /** 416 | * Decorate the root instance with a method or other value 417 | * Value is globaly accesible 418 | */ 419 | decorate (prop, value) { 420 | if (this._decorations[prop]) { 421 | this.emit('error', new Error(Constants.DECORATION_ALREADY_DEFINED)); 422 | } else if (this[prop]) { 423 | this.emit('error', new Error(Constants.OVERRIDE_BUILTIN_METHOD_NOT_ALLOWED)); 424 | } 425 | 426 | this._decorations[prop] = { plugin: this.plugin$, value }; 427 | // decorate root mostly instance 428 | this._root[prop] = value; 429 | } 430 | 431 | registerPlugins (plugins, cb) { 432 | const each = (item, next) => { 433 | // plugin has no callback 434 | if (item.register.length < 2) { 435 | item.register(item.options); 436 | return next(); 437 | } 438 | 439 | // Detect plugin timeouts 440 | const pluginTimer = setTimeout(() => { 441 | const internalError = new Errors.PluginTimeoutError(Constants.PLUGIN_TIMEOUT_ERROR); 442 | this.log.error(internalError, `Plugin: ${item.attributes.name}`); 443 | next(internalError); 444 | }, this._config.pluginTimeout); 445 | 446 | item.register(item.options, (err) => { 447 | clearTimeout(pluginTimer); 448 | next(err); 449 | }); 450 | }; 451 | 452 | // register all plugins in serie 453 | Util.serial(plugins, each, (err) => { 454 | if (err) { 455 | if (err instanceof SuperError) { 456 | err = err.rootCause || err.cause || err; 457 | } 458 | const internalError = new Errors.MostlyError(Constants.PLUGIN_REGISTRATION_ERROR).causedBy(err); 459 | this.log.error(internalError); 460 | this.emit('error', internalError); 461 | } 462 | if (_.isFunction(cb)) { 463 | cb.call(this); 464 | } 465 | }); 466 | } 467 | 468 | /** 469 | * Ready callback when Nats connected 470 | */ 471 | ready (cb) { 472 | this._transport.driver.on('error', (error) => { 473 | this.log.error(error, Constants.NATS_TRANSPORT_ERROR); 474 | this.log.error('NATS Code: \'%s\', Message: %s', error.code, error.message); 475 | 476 | // exit only on connection issues 477 | if (Constants.NATS_CONN_ERROR_CODES.indexOf(error.code) > -1) { 478 | // No callback therefore only gracefully shutdown of mostly not NATS 479 | this.close(); 480 | } 481 | }); 482 | this._transport.driver.on('permission_error', (err) => { 483 | this.log.error(err, Constants.NATS_PERMISSION_ERROR); 484 | }); 485 | this._transport.driver.on('reconnect', () => { 486 | this.log.info(Constants.NATS_TRANSPORT_RECONNECTED); 487 | }); 488 | this._transport.driver.on('reconnecting', () => { 489 | this.log.warn(Constants.NATS_TRANSPORT_RECONNECTING); 490 | }); 491 | this._transport.driver.on('disconnect', () => { 492 | this.log.warn(Constants.NATS_TRANSPORT_DISCONNECTED); 493 | }); 494 | this._transport.driver.on('close', () => { 495 | this.log.warn(Constants.NATS_TRANSPORT_CLOSED); 496 | }); 497 | this._transport.driver.on('connect', () => { 498 | this.log.info(Constants.NATS_TRANSPORT_CONNECTED); 499 | this.registerPlugins(this._pluginRegistrations, cb); 500 | }); 501 | } 502 | 503 | /** 504 | * Ready callback when Nats connected 505 | */ 506 | onError (cb) { 507 | this._transport.driver.on('error', (e) => { 508 | this.log.info(Constants.TRANSPORT_ERROR); 509 | if (_.isFunction(cb)) { 510 | cb.call(this, e); 511 | } 512 | }); 513 | } 514 | 515 | /** 516 | * Build the final payload for the response 517 | */ 518 | _buildMessage () { 519 | let result = this._response; 520 | 521 | let message = { 522 | meta: this.meta$ || {}, 523 | trace: this.trace$ || {}, 524 | request: this.request$, 525 | result: result.error? null : result.payload, 526 | error: result.error? Errio.toObject(result.error) : null 527 | }; 528 | 529 | let m = this._encoderPipeline.run(message, this); 530 | 531 | // attach encoding issues 532 | if (m.error) { 533 | let internalError = new Errors.ParseError(Constants.PAYLOAD_PARSING_ERROR).causedBy(m.error); 534 | message.error = Errio.toObject(internalError); 535 | message.result = null; 536 | 537 | // Retry to encode with issue perhaps the reason was data related 538 | m = this._encoderPipeline.run(message, this); 539 | this.log.error(internalError); 540 | this.emit('serverResponseError', m.error); 541 | } 542 | 543 | // final response 544 | this._message = m.value; 545 | } 546 | 547 | 548 | /** 549 | * Last step before the response is send to the callee. 550 | * The preResponse extension is dispatched and previous errors are evaluated. 551 | */ 552 | finish () { 553 | this._extensions.onServerPreResponse.dispatch(this, (err, val) => { 554 | return Handlers.onServerPreResponseHandler(this, err, val); 555 | }); 556 | } 557 | 558 | _actionHandler (err, resp) { 559 | const self = this; 560 | 561 | if (err) { 562 | debug('actionHandler:error', err); 563 | const errorDetails = { 564 | service: self.trace$.service, 565 | method: self._actMeta.method, 566 | app: self._config.name, 567 | ts: Util.nowHrTime() 568 | }; 569 | 570 | // collect hops 571 | if (err.hops) { 572 | err.hops.push(errorDetails); 573 | } else { 574 | err.hops = [errorDetails]; 575 | } 576 | 577 | if (err instanceof SuperError) { 578 | self._response.error = err.rootCause || err.cause || err; 579 | } else { 580 | self._response.error = err; 581 | } 582 | 583 | return self.finish(); 584 | } 585 | 586 | // assign action result 587 | self._response.payload = resp; 588 | // delete error we have payload 589 | self._response.error = null; 590 | 591 | self.finish(); 592 | } 593 | 594 | /** 595 | * Attach one handler to the topic subscriber. 596 | * With subToMany and maxMessages you control NATS specific behaviour. 597 | */ 598 | subscribe (topic, subToMany, maxMessages, queue) { 599 | const self = this; 600 | 601 | // avoid duplicate subscribers of the emit stream 602 | // we use one subscriber per topic 603 | if (self._topics[topic]) { 604 | return; 605 | } 606 | 607 | let handler = (request, replyTo) => { 608 | // create new execution context 609 | let ctx = this.createContext(); 610 | ctx._shouldCrash = false; 611 | ctx._replyTo = replyTo; 612 | ctx._topic = topic; 613 | ctx._request = new ServerRequest(request); 614 | ctx._response = new ServerResponse(); 615 | ctx._pattern = {}; 616 | ctx._actMeta = {}; 617 | ctx._isServer = true; 618 | 619 | ctx._extensions.onServerPreRequest.dispatch(ctx, (err, val) => { 620 | return Handlers.onServerPreRequestHandler(ctx, err, val); 621 | }); 622 | }; 623 | 624 | // standard pubsub with optional max proceed messages 625 | if (subToMany) { 626 | self._topics[topic] = self._transport.subscribe(topic, { 627 | max: maxMessages 628 | }, handler); 629 | } else { 630 | const queueGroup = queue || `${Constants.NATS_QUEUEGROUP_PREFIX}.${topic}`; 631 | // queue group names allow load balancing of services 632 | self._topics[topic] = self._transport.subscribe(topic, { 633 | 'queue': queueGroup, 634 | max: maxMessages 635 | }, handler); 636 | } 637 | } 638 | 639 | /** 640 | * Unsubscribe a topic or subscription id from NATS 641 | */ 642 | remove (topic, maxMessages) { 643 | const self = this; 644 | 645 | if (!topic) { 646 | let error = new Errors.MostlyError(Constants.TOPIC_SID_REQUIRED_FOR_DELETION); 647 | self.log.error(error); 648 | throw error; 649 | } 650 | 651 | if (_.isNumber(topic)) { 652 | self._transport.unsubscribe(topic, maxMessages); 653 | return true; 654 | } else { 655 | const subId = self._topics[topic]; 656 | if (subId) { 657 | self._transport.unsubscribe(subId, maxMessages); 658 | this.cleanTopic(topic); 659 | return true; 660 | } 661 | } 662 | 663 | return false; 664 | } 665 | 666 | /** 667 | * Remove topic from list and clean pattern index of topic 668 | */ 669 | cleanTopic (topic) { 670 | // release topic so we can add it again 671 | delete this._topics[topic]; 672 | // remove pattern which belongs to the topic 673 | _.each(this.list(), add => { 674 | if (add.pattern.topic === topic) { 675 | debug('remove topic', add.pattern); 676 | this.router.remove(add.pattern); 677 | } 678 | }); 679 | } 680 | 681 | /** 682 | * The topic is subscribed on NATS and can be called from any client. 683 | */ 684 | add (pattern, cb) { 685 | // check for use quick syntax for JSON objects 686 | if (_.isString(pattern)) { 687 | pattern = TinySonic(pattern); 688 | } 689 | 690 | if (!_.isObject(pattern)) { 691 | let error = new Errors.MostlyError(Constants.ADD_PATTERN_REQUIRED); 692 | this.log.error(error); 693 | throw error; 694 | } 695 | 696 | // topic is needed to subscribe on a subject in NATS 697 | if (!pattern.topic) { 698 | let error = new Errors.MostlyError(Constants.NO_TOPIC_TO_SUBSCRIBE, { 699 | pattern, 700 | app: this._config.name 701 | }); 702 | 703 | this.log.error(error); 704 | throw error; 705 | } 706 | 707 | let origPattern = _.cloneDeep(pattern); 708 | let schema = Util.extractSchema(origPattern); 709 | origPattern = Util.cleanPattern(origPattern); 710 | 711 | let actMeta = { 712 | schema: schema, 713 | pattern: origPattern, 714 | plugin: this.plugin$ 715 | }; 716 | 717 | // create message object which represent the object behind the matched pattern 718 | let addDefinition = new Add(actMeta); 719 | 720 | // set callback 721 | if (cb) { // cb is null when use chaining syntax 722 | addDefinition.action = cb; 723 | } 724 | 725 | // Support full / token wildcards in subject 726 | const bloomrunPattern = _.clone(origPattern); 727 | // Convert nats wildcard tokens to RegexExp 728 | bloomrunPattern.topic = Util.natsWildcardToRegex(bloomrunPattern.topic); 729 | 730 | let handler = this._router.lookup(bloomrunPattern); 731 | 732 | // check if pattern is already registered 733 | if (this._config.bloomrun.lookupBeforeAdd && handler) { 734 | let error = new Errors.MostlyError(Constants.PATTERN_ALREADY_IN_USE, { pattern }); 735 | this.log.error({ pattern }, error); 736 | this.emit('error', error); 737 | } 738 | 739 | // add to bloomrun 740 | this._router.add(bloomrunPattern, addDefinition); 741 | 742 | this.log.info(origPattern, Constants.ADD_ADDED); 743 | 744 | // subscribe on topic 745 | this.subscribe( 746 | pattern.topic, 747 | pattern.pubsub$, 748 | pattern.maxMessages$, 749 | pattern.queue$); 750 | 751 | return addDefinition; 752 | } 753 | 754 | _sendRequestHandler (response) { 755 | const self = this; 756 | const res = self._decoderPipeline.run(response, self); 757 | self._response.payload = res.value; 758 | self._response.error = res.error; 759 | 760 | try { 761 | // if payload is invalid (decoding error) 762 | if (self._response.error) { 763 | let error = new Errors.ParseError(Constants.PAYLOAD_PARSING_ERROR, self.errorDetails) 764 | .causedBy(self._response.error); 765 | self.log.error(error); 766 | self.emit('clientResponseError', error); 767 | 768 | self._execute(error); 769 | return; 770 | } 771 | 772 | self._extensions.onClientPostRequest.dispatch(self, (err) => { 773 | return Handlers.onClientPostRequestHandler(self, err); 774 | }); 775 | } catch (err) { 776 | let error = null; 777 | if (err instanceof SuperError) { 778 | error = err.rootCause || err.cause || err; 779 | } else { 780 | error = err; 781 | } 782 | 783 | const internalError = new Errors.FatalError(Constants.FATAL_ERROR, self.errorDetails).causedBy(err); 784 | self.log.fatal(internalError); 785 | 786 | self.emit('clientResponseError', error); 787 | 788 | // let it crash 789 | if (self._config.crashOnFatal) { 790 | self.fatal(); 791 | } 792 | } 793 | } 794 | 795 | /** 796 | * Start an action. 797 | */ 798 | act (pattern, cb) { 799 | // check for use quick syntax for JSON objects 800 | if (_.isString(pattern)) { 801 | pattern = TinySonic(pattern); 802 | } 803 | 804 | if (!_.isObject(pattern)) { 805 | let error = new Errors.MostlyError(Constants.ACT_PATTERN_REQUIRED); 806 | this.log.error(error); 807 | throw error; 808 | } 809 | 810 | // create new execution context 811 | let ctx = this.createContext(); 812 | ctx._pattern = pattern; 813 | ctx._prevContext = this; 814 | ctx._actCallback = _.isFunction(cb)? cb.bind(ctx) : null; 815 | ctx._cleanPattern = Util.cleanFromSpecialVars(pattern); 816 | ctx._response = new ClientResponse(); 817 | ctx._request = new ClientRequest(); 818 | ctx._isServer = false; 819 | ctx._execute = null; 820 | ctx._hasCallback = false; 821 | ctx._isPromisable = false; 822 | 823 | // topic is needed to subscribe on a subject in NATS 824 | if (!pattern.topic) { 825 | let error = new Errors.MostlyError(Constants.NO_TOPIC_TO_REQUEST, { pattern }); 826 | this.log.error(error); 827 | throw error; 828 | } 829 | 830 | if (cb) { 831 | ctx._hasCallback = true; 832 | if (Util.isGeneratorFunction(cb)) { 833 | ctx._actCallback = Co.wrap(cb.bind(ctx)); 834 | ctx._isPromisable = true; 835 | } else if (Util.isAsyncFunction(cb)) { 836 | ctx._actCallback = cb.bind(ctx); 837 | ctx._isPromisable = true; 838 | } else { 839 | ctx._actCallback = cb.bind(ctx); 840 | ctx._isPromisable = false; 841 | } 842 | } 843 | 844 | const promise = new Promise((resolve, reject) => { 845 | ctx._execute = (err, result) => { 846 | if (ctx._config.circuitBreaker.enabled) { 847 | const circuitBreaker = ctx._circuitBreakerMap.get(ctx.trace$.method); 848 | if (err) { 849 | circuitBreaker.failure(); 850 | } else { 851 | circuitBreaker.success(); 852 | } 853 | } 854 | 855 | if (ctx._hasCallback) { 856 | if (ctx._isPromisable) { 857 | ctx._actCallback(err, result) 858 | .then(x => resolve(x)) 859 | .catch(x => reject(x)); 860 | } else { 861 | // any return value in a callback function will fullfilled the 862 | // promise but an error will reject it 863 | const r = ctx._actCallback(err, result); 864 | if (r instanceof Error) { 865 | reject(r); 866 | } else { 867 | resolve(r); 868 | } 869 | } 870 | } else { 871 | if (err) { 872 | reject(err); 873 | } else { 874 | resolve(result); 875 | } 876 | } 877 | }; 878 | }); 879 | 880 | ctx._extensions.onClientPreRequest.dispatch(ctx, (err) => { 881 | return Handlers.onPreRequestHandler(ctx, err); 882 | }); 883 | return promise; 884 | } 885 | 886 | /** 887 | * Handle the timeout when a pattern could not be resolved. Can have different reasons: 888 | * - No one was connected at the time (service unavailable) 889 | * - Service is actually still processing the request (service takes too long) 890 | * - Service was processing the request but crashed (service error) 891 | */ 892 | handleTimeout () { 893 | const self = this; 894 | const timeout = self._pattern.timeout$ || this._config.timeout; 895 | 896 | let timeoutHandler = () => { 897 | const error = new Errors.TimeoutError(Constants.ACT_TIMEOUT_ERROR, self.errorDetails); 898 | self.log.error(error); 899 | self._response.error = error; 900 | self.emit('clientResponseError', error); 901 | self._extensions.onClientPostRequest.dispatch(self, (err) => { 902 | return Handlers.onClientTimeoutPostRequestHandler(self, err); 903 | }); 904 | }; 905 | 906 | self._transport.timeout(self._sid, timeout, 1, timeoutHandler); 907 | } 908 | 909 | /** 910 | * Create new instance of mostly but but based on the current prototype 911 | * so we are able to create a scope per act without lossing the reference to the core api. 912 | */ 913 | createContext () { 914 | const self = this; 915 | 916 | const ctx = Object.create(self); 917 | 918 | return ctx; 919 | } 920 | 921 | /** 922 | * Return the list of all registered actions 923 | */ 924 | list (pattern, options) { 925 | return this._router.list(pattern, options); 926 | } 927 | 928 | /** 929 | * Remove all registered pattern and release topic from NATS 930 | */ 931 | removeAll () { 932 | _.each(this._topics, (_, topic) => this.remove(topic)); 933 | } 934 | 935 | /** 936 | * Gracefully shutdown of all resources. 937 | * Unsubscribe all subscriptiuons and close the underlying NATS connection 938 | */ 939 | close (cb) { 940 | this._extensions.onClose.dispatch(this, (err, val) => { 941 | return Handlers.onClose(this, err, val, cb); 942 | }); 943 | } 944 | } 945 | 946 | module.exports = MostlyCore; 947 | -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | class Plugin { 2 | constructor (plugin) { 3 | this.attributes = plugin.attributes || {}; 4 | this.options = plugin.options; 5 | this.parentPluginName = plugin.parentPluginName; 6 | this.register = plugin.register; 7 | } 8 | } 9 | 10 | module.exports = Plugin; -------------------------------------------------------------------------------- /src/reply.js: -------------------------------------------------------------------------------- 1 | class Reply { 2 | constructor (request, response, extensionCallback) { 3 | this._request = request; 4 | this._response = response; 5 | this.extensionCallback = extensionCallback; 6 | } 7 | 8 | set payload (value) { 9 | this._response.payload = value; 10 | } 11 | 12 | get payload () { 13 | return this._response.payload; 14 | } 15 | 16 | set error (value) { 17 | this._response.error = value; 18 | } 19 | 20 | get error () { 21 | return this._response.error; 22 | } 23 | 24 | /** 25 | * Abort the current request and respond wih the passed value 26 | */ 27 | end (value) { 28 | if (value instanceof Error) { 29 | this.extensionCallback(value); 30 | } else { 31 | this.extensionCallback(null, value, true); 32 | } 33 | } 34 | 35 | /** 36 | * Runs through all extensions and keep the passed value to respond it 37 | */ 38 | send (value) { 39 | if (value instanceof Error) { 40 | this.extensionCallback(value); 41 | } else { 42 | this.extensionCallback(null, value); 43 | } 44 | } 45 | } 46 | 47 | module.exports = Reply; -------------------------------------------------------------------------------- /src/serializer.js: -------------------------------------------------------------------------------- 1 | function inbound (ctx) { 2 | return { 3 | id: ctx.request$.id, 4 | duration: (ctx.request$.duration / 1e6).toFixed(2) + 'ms', 5 | pattern: ctx.request$.method 6 | }; 7 | } 8 | 9 | function outbound (ctx) { 10 | return { 11 | id: ctx._message.request.id, 12 | pattern: ctx.trace$.method 13 | }; 14 | } 15 | 16 | module.exports = { 17 | inbound, 18 | outbound 19 | }; 20 | -------------------------------------------------------------------------------- /src/server-request.js: -------------------------------------------------------------------------------- 1 | class ServerRequest { 2 | 3 | constructor (payload) { 4 | this._request = {}; 5 | this._locals = {}; 6 | this.payload = payload; 7 | } 8 | 9 | get payload () { 10 | return this._request.value; 11 | } 12 | 13 | get locals () { 14 | return this._locals; 15 | } 16 | 17 | get error () { 18 | return this._request.error; 19 | } 20 | 21 | set payload (value) { 22 | this._request.value = value; 23 | } 24 | 25 | set error (error) { 26 | this._request.error = error; 27 | } 28 | } 29 | 30 | module.exports = ServerRequest; 31 | 32 | -------------------------------------------------------------------------------- /src/server-response.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | class ServerResponse { 4 | 5 | constructor () { 6 | this._response = {}; 7 | } 8 | 9 | // deprecated 10 | // end (value) { 11 | // if (value instanceof Error) { 12 | // if (_.isFunction(this.next)) { 13 | // this.next(value); 14 | // } 15 | // } else { 16 | // if (_.isFunction(this.next)) { 17 | // this.next(null, value, true); 18 | // } 19 | // } 20 | // } 21 | 22 | // deprecated 23 | // send (value) { 24 | // if (value instanceof Error) { 25 | // if (_.isFunction(this.next)) { 26 | // this.next(value); 27 | // } 28 | // } else { 29 | // if (_.isFunction(this.next)) { 30 | // this.next(null, value); 31 | // } 32 | // } 33 | // } 34 | 35 | get payload () { 36 | return this._response.value; 37 | } 38 | 39 | set payload (value) { 40 | this._response.value = value; 41 | } 42 | 43 | set error (error) { 44 | this._response.error = error; 45 | } 46 | 47 | get error () { 48 | return this._response.error; 49 | } 50 | 51 | } 52 | 53 | module.exports = ServerResponse; -------------------------------------------------------------------------------- /src/transport.js: -------------------------------------------------------------------------------- 1 | class NatsTransport { 2 | 3 | constructor (params) { 4 | this.nc = params.transport; 5 | } 6 | 7 | get driver () { 8 | return this.nc; 9 | } 10 | 11 | timeout () { 12 | return this.nc.timeout.apply(this.nc, arguments); 13 | } 14 | 15 | send () { 16 | return this.nc.publish.apply(this.nc, arguments); 17 | } 18 | 19 | close () { 20 | return this.nc.close.apply(this.nc, arguments); 21 | } 22 | 23 | flush () { 24 | return this.nc.flush.apply(this.nc, arguments); 25 | } 26 | 27 | subscribe () { 28 | return this.nc.subscribe.apply(this.nc, arguments); 29 | } 30 | 31 | unsubscribe () { 32 | return this.nc.unsubscribe.apply(this.nc, arguments); 33 | } 34 | 35 | sendRequest () { 36 | return this.nc.request.apply(this.nc, arguments); 37 | } 38 | } 39 | 40 | module.exports = NatsTransport; -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Co = require('co'); 3 | 4 | const lut = []; 5 | for (let i = 0; i < 256; i++) { lut[i] = (i < 16? '0' : '') + (i).toString(16); } 6 | 7 | class Util { 8 | 9 | static natsWildcardToRegex (subject) { 10 | let hasTokenWildcard = subject.indexOf('*') > -1; 11 | let hasFullWildcard = subject.indexOf('>') > -1; 12 | 13 | if (hasFullWildcard) { 14 | subject = subject.replace('>', '[a-zA-Z0-9\\-\\.]+'); 15 | return new RegExp('^' + subject + '$', 'i'); 16 | } else if (hasTokenWildcard) { 17 | subject = subject.replace('*', '[a-zA-Z0-9\\-]+'); 18 | return new RegExp('^' + subject + '$', 'i'); 19 | } 20 | 21 | return subject; 22 | } 23 | 24 | /** 25 | * Convert a generator or async function 26 | * to promise factory function and call the last 27 | * argument as callback 28 | * 29 | * @static 30 | * @param {any} handler 31 | * @memberof Util 32 | */ 33 | static toPromiseFact (handler) { 34 | // -1 because (req, res, next) 35 | const next = arguments[arguments.length - 1]; 36 | if (Util.isGeneratorFunction(handler)) { 37 | return Co(handler.apply(this, arguments)) 38 | .then(x => next(null, x)) 39 | .catch(next); 40 | } else if (Util.isAsyncFunction(handler)) { 41 | return handler.apply(this, arguments) 42 | .then(x => next(null, x)) 43 | .catch(next); 44 | } else { 45 | return handler; 46 | } 47 | } 48 | 49 | // Fast ID generator: e7 https://jsperf.com/uuid-generator-opt/18 50 | static randomId () { 51 | const d0 = Math.random() * 0xffffffff | 0; 52 | const d1 = Math.random() * 0xffffffff | 0; 53 | const d2 = Math.random() * 0xffffffff | 0; 54 | const d3 = Math.random() * 0xffffffff | 0; 55 | return lut[d0 & 0xff] + lut[d0 >> 8 & 0xff] + lut[d0 >> 16 & 0xff] + lut[d0 >> 24 & 0xff] + 56 | lut[d1 & 0xff] + lut[d1 >> 8 & 0xff] + lut[d1 >> 16 & 0x0f | 0x40] + lut[d1 >> 24 & 0xff] + 57 | lut[d2 & 0x3f | 0x80] + lut[d2 >> 8 & 0xff] + lut[d2 >> 16 & 0xff] + lut[d2 >> 24 & 0xff] + 58 | lut[d3 & 0xff] + lut[d3 >> 8 & 0xff] + lut[d3 >> 16 & 0xff] + lut[d3 >> 24 & 0xff]; 59 | } 60 | 61 | static serial (array, method, callback) { 62 | if (!array.length) { 63 | callback(); 64 | } else { 65 | let i = 0; 66 | const iterate = function () { 67 | const done = function (err) { 68 | if (err) { 69 | callback(err); 70 | } else { 71 | i = i + 1; 72 | if (i < array.length) { 73 | iterate(); 74 | } else { 75 | callback(); 76 | } 77 | } 78 | }; 79 | 80 | method(array[i], done, i); 81 | }; 82 | 83 | iterate(); 84 | } 85 | } 86 | 87 | /** 88 | * Executes a series of callbacks and allows to interrupt 89 | * as well as to continue with a final value 90 | * 91 | * @param {Array} array 92 | * @param {Function} method 93 | * @param {Function} callback 94 | */ 95 | static serialWithCancellation (array, method, callback) { 96 | if (!array.length) { 97 | callback(); 98 | } else { 99 | let i = 0; 100 | 101 | const iterate = function () { 102 | const done = function (err, value, abort) { 103 | if (err) { 104 | callback(err); 105 | } else if (value && abort) { 106 | callback(null, value); 107 | } else { 108 | i = i + 1; 109 | 110 | if (i < array.length) { 111 | iterate(value); 112 | } else { 113 | callback(null, value); 114 | } 115 | } 116 | }; 117 | 118 | method(array[i], done); 119 | }; 120 | 121 | iterate(); 122 | } 123 | } 124 | 125 | /** 126 | * Get high resolution time in nanoseconds 127 | */ 128 | static nowHrTime () { 129 | const hrtime = process.hrtime(); 130 | return ((+hrtime[0]) * 1e9) + (+hrtime[1]); 131 | } 132 | 133 | static extractSchema (obj) { 134 | if (obj === null) return obj; 135 | 136 | return _.pickBy(obj, function (val, prop) { 137 | return _.isObject(val); 138 | }); 139 | } 140 | 141 | static cleanPattern (obj) { 142 | if (obj === null) return obj; 143 | 144 | return _.pickBy(obj, function (val, prop) { 145 | return !_.includes(prop, '$') && !_.isObject(val); 146 | }); 147 | } 148 | 149 | static cleanFromSpecialVars (obj) { 150 | if (obj === null) return obj; 151 | 152 | return _.pickBy(obj, function (val, prop) { 153 | return !_.includes(prop, '$'); 154 | }); 155 | } 156 | 157 | static pattern (args) { 158 | if (_.isString(args)) { 159 | return args; 160 | } 161 | 162 | args = args || {}; 163 | let sb = []; 164 | _.each(args, function (v, k) { 165 | if (!~k.indexOf('$') && !_.isFunction(v) && !_.isObject(v)) { 166 | sb.push(k + ':' + v); 167 | } 168 | }); 169 | 170 | sb.sort(); 171 | 172 | return sb.join(','); 173 | } 174 | 175 | static isGeneratorFunction (obj) { 176 | var constructor = obj.constructor; 177 | if (!constructor) return false; 178 | if (constructor.name === 'GeneratorFunction' || constructor.displayName === 'GeneratorFunction') return true; 179 | return false; 180 | } 181 | 182 | static isAsyncFunction (obj) { 183 | var constructor = obj.constructor; 184 | if (!constructor) return false; 185 | if (constructor.name === 'AsyncFunction' || constructor.displayName === 'AsyncFunction') return true; 186 | return false; 187 | } 188 | } 189 | 190 | module.exports = Util; -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@7.0.0-beta.44": 6 | version "7.0.0-beta.44" 7 | resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9" 8 | dependencies: 9 | "@babel/highlight" "7.0.0-beta.44" 10 | 11 | "@babel/generator@7.0.0-beta.44": 12 | version "7.0.0-beta.44" 13 | resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42" 14 | dependencies: 15 | "@babel/types" "7.0.0-beta.44" 16 | jsesc "^2.5.1" 17 | lodash "^4.2.0" 18 | source-map "^0.5.0" 19 | trim-right "^1.0.1" 20 | 21 | "@babel/helper-function-name@7.0.0-beta.44": 22 | version "7.0.0-beta.44" 23 | resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd" 24 | dependencies: 25 | "@babel/helper-get-function-arity" "7.0.0-beta.44" 26 | "@babel/template" "7.0.0-beta.44" 27 | "@babel/types" "7.0.0-beta.44" 28 | 29 | "@babel/helper-get-function-arity@7.0.0-beta.44": 30 | version "7.0.0-beta.44" 31 | resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15" 32 | dependencies: 33 | "@babel/types" "7.0.0-beta.44" 34 | 35 | "@babel/helper-split-export-declaration@7.0.0-beta.44": 36 | version "7.0.0-beta.44" 37 | resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc" 38 | dependencies: 39 | "@babel/types" "7.0.0-beta.44" 40 | 41 | "@babel/highlight@7.0.0-beta.44": 42 | version "7.0.0-beta.44" 43 | resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5" 44 | dependencies: 45 | chalk "^2.0.0" 46 | esutils "^2.0.2" 47 | js-tokens "^3.0.0" 48 | 49 | "@babel/template@7.0.0-beta.44": 50 | version "7.0.0-beta.44" 51 | resolved "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" 52 | dependencies: 53 | "@babel/code-frame" "7.0.0-beta.44" 54 | "@babel/types" "7.0.0-beta.44" 55 | babylon "7.0.0-beta.44" 56 | lodash "^4.2.0" 57 | 58 | "@babel/traverse@7.0.0-beta.44": 59 | version "7.0.0-beta.44" 60 | resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966" 61 | dependencies: 62 | "@babel/code-frame" "7.0.0-beta.44" 63 | "@babel/generator" "7.0.0-beta.44" 64 | "@babel/helper-function-name" "7.0.0-beta.44" 65 | "@babel/helper-split-export-declaration" "7.0.0-beta.44" 66 | "@babel/types" "7.0.0-beta.44" 67 | babylon "7.0.0-beta.44" 68 | debug "^3.1.0" 69 | globals "^11.1.0" 70 | invariant "^2.2.0" 71 | lodash "^4.2.0" 72 | 73 | "@babel/types@7.0.0-beta.44": 74 | version "7.0.0-beta.44" 75 | resolved "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757" 76 | dependencies: 77 | esutils "^2.0.2" 78 | lodash "^4.2.0" 79 | to-fast-properties "^2.0.0" 80 | 81 | abbrev@1: 82 | version "1.1.1" 83 | resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 84 | 85 | abbrev@1.0.x: 86 | version "1.0.9" 87 | resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" 88 | 89 | align-text@^0.1.1, align-text@^0.1.3: 90 | version "0.1.4" 91 | resolved "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" 92 | dependencies: 93 | kind-of "^3.0.2" 94 | longest "^1.0.1" 95 | repeat-string "^1.5.2" 96 | 97 | amdefine@>=0.0.4: 98 | version "1.0.1" 99 | resolved "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" 100 | 101 | ansi-styles@^3.2.1: 102 | version "3.2.1" 103 | resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 104 | dependencies: 105 | color-convert "^1.9.0" 106 | 107 | argparse@^1.0.7: 108 | version "1.0.10" 109 | resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 110 | dependencies: 111 | sprintf-js "~1.0.2" 112 | 113 | async@1.x, async@^1.4.0: 114 | version "1.5.2" 115 | resolved "https://registry.npmjs.org/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" 116 | 117 | babel-eslint@^8.2.2: 118 | version "8.2.6" 119 | resolved "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.6.tgz#6270d0c73205628067c0f7ae1693a9e797acefd9" 120 | dependencies: 121 | "@babel/code-frame" "7.0.0-beta.44" 122 | "@babel/traverse" "7.0.0-beta.44" 123 | "@babel/types" "7.0.0-beta.44" 124 | babylon "7.0.0-beta.44" 125 | eslint-scope "3.7.1" 126 | eslint-visitor-keys "^1.0.0" 127 | 128 | babylon@7.0.0-beta.44: 129 | version "7.0.0-beta.44" 130 | resolved "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d" 131 | 132 | balanced-match@^1.0.0: 133 | version "1.0.0" 134 | resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 135 | 136 | bloomrun@^3.0.4: 137 | version "3.0.6" 138 | resolved "https://registry.npmjs.org/bloomrun/-/bloomrun-3.0.6.tgz#aae94f120b1f832275a6ce2188c6f40491f1b9a2" 139 | dependencies: 140 | sorted-array-functions "^1.0.0" 141 | 142 | boom@5.x.x: 143 | version "5.2.0" 144 | resolved "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" 145 | dependencies: 146 | hoek "4.x.x" 147 | 148 | brace-expansion@^1.1.7: 149 | version "1.1.11" 150 | resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 151 | dependencies: 152 | balanced-match "^1.0.0" 153 | concat-map "0.0.1" 154 | 155 | browser-stdout@1.3.0: 156 | version "1.3.0" 157 | resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 158 | 159 | camelcase@^1.0.2: 160 | version "1.2.1" 161 | resolved "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" 162 | 163 | center-align@^0.1.1: 164 | version "0.1.3" 165 | resolved "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" 166 | dependencies: 167 | align-text "^0.1.3" 168 | lazy-cache "^1.0.3" 169 | 170 | chalk@^2.0.0, chalk@^2.4.1: 171 | version "2.4.1" 172 | resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 173 | dependencies: 174 | ansi-styles "^3.2.1" 175 | escape-string-regexp "^1.0.5" 176 | supports-color "^5.3.0" 177 | 178 | cliui@^2.1.0: 179 | version "2.1.0" 180 | resolved "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" 181 | dependencies: 182 | center-align "^0.1.1" 183 | right-align "^0.1.1" 184 | wordwrap "0.0.2" 185 | 186 | co@^4.6.0: 187 | version "4.6.0" 188 | resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" 189 | 190 | color-convert@^1.9.0: 191 | version "1.9.2" 192 | resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" 193 | dependencies: 194 | color-name "1.1.1" 195 | 196 | color-name@1.1.1: 197 | version "1.1.1" 198 | resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" 199 | 200 | commander@2.9.0: 201 | version "2.9.0" 202 | resolved "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 203 | dependencies: 204 | graceful-readlink ">= 1.0.0" 205 | 206 | concat-map@0.0.1: 207 | version "0.0.1" 208 | resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 209 | 210 | core-util-is@~1.0.0: 211 | version "1.0.2" 212 | resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 213 | 214 | debug@2.6.8: 215 | version "2.6.8" 216 | resolved "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" 217 | dependencies: 218 | ms "2.0.0" 219 | 220 | debug@^2.6.6: 221 | version "2.6.9" 222 | resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 223 | dependencies: 224 | ms "2.0.0" 225 | 226 | debug@^3.1.0: 227 | version "3.1.0" 228 | resolved "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 229 | dependencies: 230 | ms "2.0.0" 231 | 232 | decamelize@^1.0.0: 233 | version "1.2.0" 234 | resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 235 | 236 | deep-is@~0.1.3: 237 | version "0.1.3" 238 | resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 239 | 240 | diff@3.2.0: 241 | version "3.2.0" 242 | resolved "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" 243 | 244 | end-of-stream@^1.1.0: 245 | version "1.4.1" 246 | resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" 247 | dependencies: 248 | once "^1.4.0" 249 | 250 | errio@^1.2.2: 251 | version "1.2.2" 252 | resolved "https://registry.npmjs.org/errio/-/errio-1.2.2.tgz#2430afa66fa6dc363db588c3d83c4407a85d0255" 253 | 254 | escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: 255 | version "1.0.5" 256 | resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 257 | 258 | escodegen@1.8.x: 259 | version "1.8.1" 260 | resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" 261 | dependencies: 262 | esprima "^2.7.1" 263 | estraverse "^1.9.1" 264 | esutils "^2.0.2" 265 | optionator "^0.8.1" 266 | optionalDependencies: 267 | source-map "~0.2.0" 268 | 269 | eslint-if-supported@^1.0.1: 270 | version "1.0.1" 271 | resolved "https://registry.npmjs.org/eslint-if-supported/-/eslint-if-supported-1.0.1.tgz#c2a6583b134a2d4d838eae35ee4b8bc8d37e856e" 272 | 273 | eslint-plugin-promise@^3.5.0: 274 | version "3.8.0" 275 | resolved "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz#65ebf27a845e3c1e9d6f6a5622ddd3801694b621" 276 | 277 | eslint-scope@3.7.1: 278 | version "3.7.1" 279 | resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" 280 | dependencies: 281 | esrecurse "^4.1.0" 282 | estraverse "^4.1.1" 283 | 284 | eslint-visitor-keys@^1.0.0: 285 | version "1.0.0" 286 | resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" 287 | 288 | esprima@2.7.x, esprima@^2.7.1: 289 | version "2.7.3" 290 | resolved "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" 291 | 292 | esprima@^4.0.0: 293 | version "4.0.1" 294 | resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 295 | 296 | esrecurse@^4.1.0: 297 | version "4.2.1" 298 | resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" 299 | dependencies: 300 | estraverse "^4.1.0" 301 | 302 | estraverse@^1.9.1: 303 | version "1.9.3" 304 | resolved "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" 305 | 306 | estraverse@^4.1.0, estraverse@^4.1.1: 307 | version "4.2.0" 308 | resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 309 | 310 | esutils@^2.0.2: 311 | version "2.0.2" 312 | resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 313 | 314 | fast-json-parse@^1.0.3: 315 | version "1.0.3" 316 | resolved "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" 317 | 318 | fast-levenshtein@~2.0.4: 319 | version "2.0.6" 320 | resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 321 | 322 | fast-safe-stringify@^1.0.8, fast-safe-stringify@^1.1.13, fast-safe-stringify@^1.2.3: 323 | version "1.2.3" 324 | resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-1.2.3.tgz#9fe22c37fb2f7f86f06b8f004377dbf8f1ee7bc1" 325 | 326 | flatstr@^1.0.5: 327 | version "1.0.8" 328 | resolved "https://registry.npmjs.org/flatstr/-/flatstr-1.0.8.tgz#0e849229751f2b9f6a0919f8e81e1229e84ba901" 329 | 330 | fs.realpath@^1.0.0: 331 | version "1.0.0" 332 | resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 333 | 334 | glob@7.1.1: 335 | version "7.1.1" 336 | resolved "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 337 | dependencies: 338 | fs.realpath "^1.0.0" 339 | inflight "^1.0.4" 340 | inherits "2" 341 | minimatch "^3.0.2" 342 | once "^1.3.0" 343 | path-is-absolute "^1.0.0" 344 | 345 | glob@^5.0.15: 346 | version "5.0.15" 347 | resolved "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" 348 | dependencies: 349 | inflight "^1.0.4" 350 | inherits "2" 351 | minimatch "2 || 3" 352 | once "^1.3.0" 353 | path-is-absolute "^1.0.0" 354 | 355 | globals@^11.1.0: 356 | version "11.7.0" 357 | resolved "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" 358 | 359 | "graceful-readlink@>= 1.0.0": 360 | version "1.0.1" 361 | resolved "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 362 | 363 | growl@1.9.2: 364 | version "1.9.2" 365 | resolved "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" 366 | 367 | handlebars@^4.0.1: 368 | version "4.0.11" 369 | resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" 370 | dependencies: 371 | async "^1.4.0" 372 | optimist "^0.6.1" 373 | source-map "^0.4.4" 374 | optionalDependencies: 375 | uglify-js "^2.6" 376 | 377 | has-flag@^1.0.0: 378 | version "1.0.0" 379 | resolved "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 380 | 381 | has-flag@^3.0.0: 382 | version "3.0.0" 383 | resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 384 | 385 | he@1.1.1: 386 | version "1.1.1" 387 | resolved "https://registry.npmjs.org/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" 388 | 389 | heavy@^4.0.3: 390 | version "4.0.4" 391 | resolved "https://registry.npmjs.org/heavy/-/heavy-4.0.4.tgz#36c91336c00ccfe852caa4d153086335cd2f00e9" 392 | dependencies: 393 | boom "5.x.x" 394 | hoek "4.x.x" 395 | joi "10.x.x" 396 | 397 | hoek@4.x.x: 398 | version "4.2.1" 399 | resolved "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" 400 | 401 | inflight@^1.0.4: 402 | version "1.0.6" 403 | resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 404 | dependencies: 405 | once "^1.3.0" 406 | wrappy "1" 407 | 408 | inherits@2, inherits@~2.0.3: 409 | version "2.0.3" 410 | resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 411 | 412 | invariant@^2.2.0: 413 | version "2.2.4" 414 | resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" 415 | dependencies: 416 | loose-envify "^1.0.0" 417 | 418 | is-buffer@^1.1.5: 419 | version "1.1.6" 420 | resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 421 | 422 | isarray@~1.0.0: 423 | version "1.0.0" 424 | resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 425 | 426 | isemail@2.x.x: 427 | version "2.2.1" 428 | resolved "https://registry.npmjs.org/isemail/-/isemail-2.2.1.tgz#0353d3d9a62951080c262c2aa0a42b8ea8e9e2a6" 429 | 430 | isexe@^2.0.0: 431 | version "2.0.0" 432 | resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 433 | 434 | istanbul@^0.4.5: 435 | version "0.4.5" 436 | resolved "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" 437 | dependencies: 438 | abbrev "1.0.x" 439 | async "1.x" 440 | escodegen "1.8.x" 441 | esprima "2.7.x" 442 | glob "^5.0.15" 443 | handlebars "^4.0.1" 444 | js-yaml "3.x" 445 | mkdirp "0.5.x" 446 | nopt "3.x" 447 | once "1.x" 448 | resolve "1.1.x" 449 | supports-color "^3.1.0" 450 | which "^1.1.1" 451 | wordwrap "^1.0.0" 452 | 453 | items@2.x.x: 454 | version "2.1.1" 455 | resolved "https://registry.npmjs.org/items/-/items-2.1.1.tgz#8bd16d9c83b19529de5aea321acaada78364a198" 456 | 457 | joi@10.x.x: 458 | version "10.6.0" 459 | resolved "https://registry.npmjs.org/joi/-/joi-10.6.0.tgz#52587f02d52b8b75cdb0c74f0b164a191a0e1fc2" 460 | dependencies: 461 | hoek "4.x.x" 462 | isemail "2.x.x" 463 | items "2.x.x" 464 | topo "2.x.x" 465 | 466 | js-tokens@^3.0.0: 467 | version "3.0.2" 468 | resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 469 | 470 | "js-tokens@^3.0.0 || ^4.0.0": 471 | version "4.0.0" 472 | resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 473 | 474 | js-yaml@3.x: 475 | version "3.12.0" 476 | resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" 477 | dependencies: 478 | argparse "^1.0.7" 479 | esprima "^4.0.0" 480 | 481 | jsesc@^2.5.1: 482 | version "2.5.1" 483 | resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe" 484 | 485 | json3@3.3.2: 486 | version "3.3.2" 487 | resolved "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 488 | 489 | kind-of@^3.0.2: 490 | version "3.2.2" 491 | resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" 492 | dependencies: 493 | is-buffer "^1.1.5" 494 | 495 | lazy-cache@^1.0.3: 496 | version "1.0.4" 497 | resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" 498 | 499 | levn@~0.3.0: 500 | version "0.3.0" 501 | resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 502 | dependencies: 503 | prelude-ls "~1.1.2" 504 | type-check "~0.3.2" 505 | 506 | lodash._baseassign@^3.0.0: 507 | version "3.2.0" 508 | resolved "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 509 | dependencies: 510 | lodash._basecopy "^3.0.0" 511 | lodash.keys "^3.0.0" 512 | 513 | lodash._basecopy@^3.0.0: 514 | version "3.0.1" 515 | resolved "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 516 | 517 | lodash._basecreate@^3.0.0: 518 | version "3.0.3" 519 | resolved "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" 520 | 521 | lodash._getnative@^3.0.0: 522 | version "3.9.1" 523 | resolved "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 524 | 525 | lodash._isiterateecall@^3.0.0: 526 | version "3.0.9" 527 | resolved "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 528 | 529 | lodash.create@3.1.1: 530 | version "3.1.1" 531 | resolved "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" 532 | dependencies: 533 | lodash._baseassign "^3.0.0" 534 | lodash._basecreate "^3.0.0" 535 | lodash._isiterateecall "^3.0.0" 536 | 537 | lodash.isarguments@^3.0.0: 538 | version "3.1.0" 539 | resolved "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 540 | 541 | lodash.isarray@^3.0.0: 542 | version "3.0.4" 543 | resolved "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 544 | 545 | lodash.keys@^3.0.0: 546 | version "3.1.2" 547 | resolved "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 548 | dependencies: 549 | lodash._getnative "^3.0.0" 550 | lodash.isarguments "^3.0.0" 551 | lodash.isarray "^3.0.0" 552 | 553 | lodash@^4.17.4, lodash@^4.2.0: 554 | version "4.17.10" 555 | resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" 556 | 557 | longest@^1.0.1: 558 | version "1.0.1" 559 | resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" 560 | 561 | loose-envify@^1.0.0: 562 | version "1.4.0" 563 | resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 564 | dependencies: 565 | js-tokens "^3.0.0 || ^4.0.0" 566 | 567 | "minimatch@2 || 3", minimatch@^3.0.2: 568 | version "3.0.4" 569 | resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 570 | dependencies: 571 | brace-expansion "^1.1.7" 572 | 573 | minimist@0.0.8: 574 | version "0.0.8" 575 | resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 576 | 577 | minimist@~0.0.1: 578 | version "0.0.10" 579 | resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" 580 | 581 | mkdirp@0.5.1, mkdirp@0.5.x: 582 | version "0.5.1" 583 | resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 584 | dependencies: 585 | minimist "0.0.8" 586 | 587 | mocha@^3.3.0: 588 | version "3.5.3" 589 | resolved "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" 590 | dependencies: 591 | browser-stdout "1.3.0" 592 | commander "2.9.0" 593 | debug "2.6.8" 594 | diff "3.2.0" 595 | escape-string-regexp "1.0.5" 596 | glob "7.1.1" 597 | growl "1.9.2" 598 | he "1.1.1" 599 | json3 "3.3.2" 600 | lodash.create "3.1.1" 601 | mkdirp "0.5.1" 602 | supports-color "3.1.2" 603 | 604 | ms@2.0.0: 605 | version "2.0.0" 606 | resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 607 | 608 | nats@^1.0.0: 609 | version "1.0.0" 610 | resolved "https://registry.npmjs.org/nats/-/nats-1.0.0.tgz#6b1681c46ec08ea0b92dfed1abe413692b44c35f" 611 | dependencies: 612 | nuid "^1.0.0" 613 | 614 | nopt@3.x: 615 | version "3.0.6" 616 | resolved "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" 617 | dependencies: 618 | abbrev "1" 619 | 620 | nuid@^1.0.0: 621 | version "1.0.0" 622 | resolved "https://registry.npmjs.org/nuid/-/nuid-1.0.0.tgz#04ecc21be41cc321940a9089984382f3bdc61638" 623 | 624 | once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: 625 | version "1.4.0" 626 | resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 627 | dependencies: 628 | wrappy "1" 629 | 630 | optimist@^0.6.1: 631 | version "0.6.1" 632 | resolved "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" 633 | dependencies: 634 | minimist "~0.0.1" 635 | wordwrap "~0.0.2" 636 | 637 | optionator@^0.8.1: 638 | version "0.8.2" 639 | resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 640 | dependencies: 641 | deep-is "~0.1.3" 642 | fast-levenshtein "~2.0.4" 643 | levn "~0.3.0" 644 | prelude-ls "~1.1.2" 645 | type-check "~0.3.2" 646 | wordwrap "~1.0.0" 647 | 648 | path-is-absolute@^1.0.0: 649 | version "1.0.1" 650 | resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 651 | 652 | pino-std-serializers@^2.0.0: 653 | version "2.1.0" 654 | resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.1.0.tgz#01953dcaecd5f43b331ecf2e312a49c9fd64851c" 655 | 656 | pino@^4.3.0: 657 | version "4.17.3" 658 | resolved "https://registry.npmjs.org/pino/-/pino-4.17.3.tgz#3536ea36f6258356ba8891d18dbf1afed41fcf09" 659 | dependencies: 660 | chalk "^2.4.1" 661 | fast-json-parse "^1.0.3" 662 | fast-safe-stringify "^1.2.3" 663 | flatstr "^1.0.5" 664 | pino-std-serializers "^2.0.0" 665 | pump "^3.0.0" 666 | quick-format-unescaped "^1.1.2" 667 | split2 "^2.2.0" 668 | 669 | prelude-ls@~1.1.2: 670 | version "1.1.2" 671 | resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 672 | 673 | process-nextick-args@~2.0.0: 674 | version "2.0.0" 675 | resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" 676 | 677 | pump@^3.0.0: 678 | version "3.0.0" 679 | resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" 680 | dependencies: 681 | end-of-stream "^1.1.0" 682 | once "^1.3.1" 683 | 684 | quick-format-unescaped@^1.1.2: 685 | version "1.1.2" 686 | resolved "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-1.1.2.tgz#0ca581de3174becef25ac3c2e8956342381db698" 687 | dependencies: 688 | fast-safe-stringify "^1.0.8" 689 | 690 | readable-stream@^2.1.5: 691 | version "2.3.6" 692 | resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 693 | dependencies: 694 | core-util-is "~1.0.0" 695 | inherits "~2.0.3" 696 | isarray "~1.0.0" 697 | process-nextick-args "~2.0.0" 698 | safe-buffer "~5.1.1" 699 | string_decoder "~1.1.1" 700 | util-deprecate "~1.0.1" 701 | 702 | repeat-string@^1.5.2: 703 | version "1.6.1" 704 | resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 705 | 706 | resolve@1.1.x: 707 | version "1.1.7" 708 | resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 709 | 710 | right-align@^0.1.1: 711 | version "0.1.3" 712 | resolved "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" 713 | dependencies: 714 | align-text "^0.1.1" 715 | 716 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 717 | version "5.1.2" 718 | resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 719 | 720 | semver@^5.4.1: 721 | version "5.5.0" 722 | resolved "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" 723 | 724 | sorted-array-functions@^1.0.0: 725 | version "1.2.0" 726 | resolved "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.2.0.tgz#43265b21d6e985b7df31621b1c11cc68d8efc7c3" 727 | 728 | source-map@^0.4.4: 729 | version "0.4.4" 730 | resolved "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" 731 | dependencies: 732 | amdefine ">=0.0.4" 733 | 734 | source-map@^0.5.0, source-map@~0.5.1: 735 | version "0.5.7" 736 | resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 737 | 738 | source-map@~0.2.0: 739 | version "0.2.0" 740 | resolved "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" 741 | dependencies: 742 | amdefine ">=0.0.4" 743 | 744 | split2@^2.2.0: 745 | version "2.2.0" 746 | resolved "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" 747 | dependencies: 748 | through2 "^2.0.2" 749 | 750 | sprintf-js@~1.0.2: 751 | version "1.0.3" 752 | resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 753 | 754 | string_decoder@~1.1.1: 755 | version "1.1.1" 756 | resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 757 | dependencies: 758 | safe-buffer "~5.1.0" 759 | 760 | super-error@^2.0.0: 761 | version "2.2.0" 762 | resolved "https://registry.npmjs.org/super-error/-/super-error-2.2.0.tgz#d850cf4899fa7880e2f36641e301c397b0eaa108" 763 | 764 | supports-color@3.1.2: 765 | version "3.1.2" 766 | resolved "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" 767 | dependencies: 768 | has-flag "^1.0.0" 769 | 770 | supports-color@^3.1.0: 771 | version "3.2.3" 772 | resolved "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" 773 | dependencies: 774 | has-flag "^1.0.0" 775 | 776 | supports-color@^5.3.0: 777 | version "5.4.0" 778 | resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 779 | dependencies: 780 | has-flag "^3.0.0" 781 | 782 | through2@^2.0.2: 783 | version "2.0.3" 784 | resolved "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" 785 | dependencies: 786 | readable-stream "^2.1.5" 787 | xtend "~4.0.1" 788 | 789 | tinysonic@^1.2.0: 790 | version "1.3.0" 791 | resolved "https://registry.npmjs.org/tinysonic/-/tinysonic-1.3.0.tgz#3f453638501c099db59d17a3f5c1e2a4a9c6d85a" 792 | 793 | to-fast-properties@^2.0.0: 794 | version "2.0.0" 795 | resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" 796 | 797 | topo@2.x.x: 798 | version "2.0.2" 799 | resolved "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz#cd5615752539057c0dc0491a621c3bc6fbe1d182" 800 | dependencies: 801 | hoek "4.x.x" 802 | 803 | trim-right@^1.0.1: 804 | version "1.0.1" 805 | resolved "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" 806 | 807 | type-check@~0.3.2: 808 | version "0.3.2" 809 | resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 810 | dependencies: 811 | prelude-ls "~1.1.2" 812 | 813 | uglify-js@^2.6: 814 | version "2.8.29" 815 | resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" 816 | dependencies: 817 | source-map "~0.5.1" 818 | yargs "~3.10.0" 819 | optionalDependencies: 820 | uglify-to-browserify "~1.0.0" 821 | 822 | uglify-to-browserify@~1.0.0: 823 | version "1.0.2" 824 | resolved "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" 825 | 826 | util-deprecate@~1.0.1: 827 | version "1.0.2" 828 | resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 829 | 830 | which@^1.1.1: 831 | version "1.3.1" 832 | resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 833 | dependencies: 834 | isexe "^2.0.0" 835 | 836 | window-size@0.1.0: 837 | version "0.1.0" 838 | resolved "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" 839 | 840 | wordwrap@0.0.2: 841 | version "0.0.2" 842 | resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" 843 | 844 | wordwrap@^1.0.0, wordwrap@~1.0.0: 845 | version "1.0.0" 846 | resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 847 | 848 | wordwrap@~0.0.2: 849 | version "0.0.3" 850 | resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" 851 | 852 | wrappy@1: 853 | version "1.0.2" 854 | resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 855 | 856 | xtend@~4.0.1: 857 | version "4.0.1" 858 | resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 859 | 860 | yargs@~3.10.0: 861 | version "3.10.0" 862 | resolved "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" 863 | dependencies: 864 | camelcase "^1.0.2" 865 | cliui "^2.1.0" 866 | decamelize "^1.0.0" 867 | window-size "0.1.0" 868 | --------------------------------------------------------------------------------