diff --git a/.nvmrc b/.nvmrc index d578315..9da0a09 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -8.1.3 \ No newline at end of file +8.4.0 \ No newline at end of file diff --git a/README.md b/README.md index 5c069f5..c0ff2f3 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,13 @@ To set up your development environment: [Back to top](#table-of-contents) ## Change History +* v1.0.0-rc.7 (2017-08-28) + * Code refactoring with only functional programming + * Improve errors management + * Allow HTTPS and WSS connections + * Add service for HTTP and WS extension to use it in DI + * Latest packages' versions + * Documentation * v1.0.0-rc.6 (2017-07-16) * DI/req performance issue temp. fix * v1.0.0-rc.5 (2017-07-11) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5bc3ea0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2766 @@ +{ + "name": "@hapiness/core", + "version": "1.0.0-rc.7", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/boom": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/@types/boom/-/boom-4.3.7.tgz", + "integrity": "sha512-z6CT5iHAtBUib2JQwwiGVKHESAk5Elfwn0HFlpLC4rwpTtgCa1P5OCFzfCmF3vSpgYoZKm+3fh6sgFWJwwb8fw==" + }, + "@types/catbox": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/catbox/-/catbox-7.1.3.tgz", + "integrity": "sha512-gpCXh6swH1/hUuWuozT70FJNgDj9Z0LV6P5sDW7/LPONolPiQYpq+XueImVQ9jyKips4k+Vi3Z7ooeHf8B8/qA==", + "requires": { + "@types/boom": "4.3.7" + } + }, + "@types/fs-extra": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-4.0.0.tgz", + "integrity": "sha512-PlKJw6ujJXLJjbvB3T0UCbY3jibKM6/Ya5cc9j1q+mYDeK3aR4Dp+20ZwxSuvJr9mIoPxp7+IL4aMOEvsscRTA==", + "dev": true, + "requires": { + "@types/node": "8.0.25" + } + }, + "@types/hapi": { + "version": "16.1.9", + "resolved": "https://registry.npmjs.org/@types/hapi/-/hapi-16.1.9.tgz", + "integrity": "sha512-t0oOkiGguDhnE10fiFaIkWZRlQymNwq3U/Ql81x+UpDWHFPmpdhebXcKcncUhXueF43lOwlZbscVucMu3lO4zQ==", + "requires": { + "@types/boom": "4.3.7", + "@types/catbox": "7.1.3", + "@types/joi": "10.4.1", + "@types/mimos": "3.0.1", + "@types/node": "8.0.25", + "@types/podium": "1.0.0", + "@types/shot": "3.4.0" + } + }, + "@types/hoek": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/hoek/-/hoek-4.1.3.tgz", + "integrity": "sha1-0ZgtSPsNKg5dfp2Rg4Jk2OQo0zc=" + }, + "@types/joi": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@types/joi/-/joi-10.4.1.tgz", + "integrity": "sha512-tZ0q9R3XaEbBD6WW4mdBhRWUeG2ehVC4UqPAeIE0joE3BA/VfqdLF2asGkgQnC6xyU9VomDnaHS0IF5sjpr0eg==" + }, + "@types/mime-db": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@types/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-m8AUof0f30dknBpUxt15ZrgoR5I=" + }, + "@types/mimos": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mimos/-/mimos-3.0.1.tgz", + "integrity": "sha512-MATIRH4VMIJki8lcYUZdNQEHuAG7iQ1FWwoLgxV+4fUOly2xZYdhHtGgvQyWiTeJqq2tZbE0nOOgZD6pR0FpNQ==", + "requires": { + "@types/mime-db": "1.27.0" + } + }, + "@types/node": { + "version": "8.0.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.25.tgz", + "integrity": "sha512-zT+t9841g1HsjLtPMCYxmb1U4pcZ2TOegAKiomlmj6bIziuaEYHUavxLE9NRwdntY0vOCrgHho6OXjDX7fm/Kw==" + }, + "@types/podium": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/podium/-/podium-1.0.0.tgz", + "integrity": "sha1-v6ohUb4rHWEJzGn3+qnawsujuyA=" + }, + "@types/shot": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/shot/-/shot-3.4.0.tgz", + "integrity": "sha1-RZR3xRh9Pr0wNmCrCZ5+ng87ZW8=", + "requires": { + "@types/node": "8.0.25" + } + }, + "@types/websocket": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-0.0.34.tgz", + "integrity": "sha512-cvN32rSoC4aXpXHG2Gf8/f8/gtyA7PmE38E3ENsMymFPgoG8xD/kH/KAVhTINCZTPqmgaWN86c1MitMEsuHjnQ==", + "requires": { + "@types/node": "8.0.25" + } + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "accept": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/accept/-/accept-2.1.4.tgz", + "integrity": "sha1-iHr1TO7lx/RDBGGXHsQAxh0JrLs=", + "requires": { + "boom": "5.2.0", + "hoek": "4.2.0" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ammo": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/ammo/-/ammo-2.0.4.tgz", + "integrity": "sha1-v4CqshFpjqePY+9efxE91dnokX8=", + "requires": { + "boom": "5.2.0", + "hoek": "4.2.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "dev": true, + "requires": { + "default-require-extensions": "1.0.0" + } + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "b64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/b64/-/b64-3.0.3.tgz", + "integrity": "sha512-Pbeh0i6OLubPJdIdCepn8ZQHwN2MWznZHbHABSTEfQ706ie+yuxNSaPdqX1xRatT6WanaS1EazMiSg0NUW2XxQ==" + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "babel-generator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", + "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", + "dev": true, + "requires": { + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.4", + "source-map": "0.5.7", + "trim-right": "1.0.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "2.5.0", + "regenerator-runtime": "0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.8", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.4" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", + "dev": true + }, + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "requires": { + "hoek": "4.2.0" + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "call": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/call/-/call-4.0.2.tgz", + "integrity": "sha1-33b19R7o3Ui4VqyEAPfmnm1zmcQ=", + "requires": { + "boom": "5.2.0", + "hoek": "4.2.0" + } + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "optional": true + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true + }, + "catbox": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/catbox/-/catbox-7.1.5.tgz", + "integrity": "sha512-4fui5lELzqZ+9cnaAP/BcqXTH6LvWLBRtFhJ0I4FfgfXiSaZcf6k9m9dqOyChiTxNYtvLk7ZMYSf7ahMq3bf5A==", + "requires": { + "boom": "5.2.0", + "hoek": "4.2.0", + "joi": "10.6.0" + } + }, + "catbox-memory": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/catbox-memory/-/catbox-memory-2.0.4.tgz", + "integrity": "sha1-Qz4lWQLK9UIz0ShkKcj03xToItU=", + "requires": { + "hoek": "4.2.0" + } + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "optional": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true, + "optional": true + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "component-emitter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", + "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "content": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/content/-/content-3.0.5.tgz", + "integrity": "sha512-MQVYZuNnm5N0xalwtRGlZrcKFwgE6VKXCEh3XkCeoWUo3gL5BS52UiqswRvAwQW1ocfdCNkEKI5uy0pNGax+IQ==", + "requires": { + "boom": "5.2.0" + } + }, + "cookiejar": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.1.tgz", + "integrity": "sha1-PRJ1L2rfaKiS8zJDNJK9WBK7Zo8=", + "dev": true + }, + "core-js": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.0.tgz", + "integrity": "sha1-VpwFCRi+ZIazg3VSAorgRmtxcIY=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "coveralls": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-2.13.1.tgz", + "integrity": "sha1-1wu5rMGDXsTwY/+drFQjwXsR8Xg=", + "dev": true, + "requires": { + "js-yaml": "3.6.1", + "lcov-parse": "0.0.10", + "log-driver": "1.2.5", + "minimist": "1.2.0", + "request": "2.79.0" + } + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "requires": { + "boom": "5.2.0" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "debug": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.0.1.tgz", + "integrity": "sha512-6nVc6S36qbt/mutyt+UGMnawAMrPDZUPQjRZI3FS9tCtDRhvxJbK79unYBLPi+z5SLXQ3ftoVBFCblQtNSls8w==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "default-require-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "dev": true, + "requires": { + "strip-bom": "2.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "requires": { + "glob": "7.1.2", + "minimatch": "3.0.4" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.16" + } + }, + "formatio": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", + "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", + "dev": true, + "requires": { + "samsam": "1.1.2" + } + }, + "formidable": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", + "integrity": "sha1-Kz9MQRy7X91pXESEPiojUUpDIxo=", + "dev": true + }, + "fs-extra": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.1.tgz", + "integrity": "sha1-f8DGyJV/mD9X8waiTlud3Y0N2IA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "3.0.1", + "universalify": "0.1.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "1.0.2" + } + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, + "handlebars": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", + "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", + "dev": true, + "requires": { + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "hapi": { + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/hapi/-/hapi-16.5.2.tgz", + "integrity": "sha512-8aXEiC2IT1bfF/5RYz9LtWqm99oGVDd2JcQ3KRRvFGDngEE35kZl9Bk2jproY97fBrmAlvkNrmQKWf3jb2lNxw==", + "requires": { + "accept": "2.1.4", + "ammo": "2.0.4", + "boom": "5.2.0", + "call": "4.0.2", + "catbox": "7.1.5", + "catbox-memory": "2.0.4", + "cryptiles": "3.1.2", + "heavy": "4.0.4", + "hoek": "4.2.0", + "iron": "4.0.5", + "items": "2.1.1", + "joi": "10.6.0", + "mimos": "3.0.3", + "podium": "1.3.0", + "shot": "3.4.2", + "statehood": "5.0.3", + "subtext": "5.0.0", + "topo": "2.0.2" + } + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.11.0", + "is-my-json-valid": "2.16.1", + "pinkie-promise": "2.0.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + }, + "dependencies": { + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + } + } + }, + "heavy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/heavy/-/heavy-4.0.4.tgz", + "integrity": "sha1-NskTNsAMz+hSyqTRUwhjNc0vAOk=", + "requires": { + "boom": "5.2.0", + "hoek": "4.2.0", + "joi": "10.6.0" + } + }, + "hoek": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" + }, + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "invariant": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "dev": true, + "requires": { + "loose-envify": "1.3.1" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "iron": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/iron/-/iron-4.0.5.tgz", + "integrity": "sha1-TwQszri5c480a1mqc0yDqJvDFCg=", + "requires": { + "boom": "5.2.0", + "cryptiles": "3.1.2", + "hoek": "4.2.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-my-json-valid": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", + "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", + "dev": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isemail": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-2.2.1.tgz", + "integrity": "sha1-A1PT2aYpUQgMJiwqoKQrjqjp4qY=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul": { + "version": "1.1.0-alpha.1", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-1.1.0-alpha.1.tgz", + "integrity": "sha1-eBeVZWAYohdMX2DzZ+5dNhy1e3c=", + "dev": true, + "requires": { + "abbrev": "1.0.9", + "async": "1.5.2", + "istanbul-api": "1.1.13", + "js-yaml": "3.6.1", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "which": "1.3.0", + "wordwrap": "1.0.0" + } + }, + "istanbul-api": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.1.13.tgz", + "integrity": "sha1-cZf2RBNgDr3+xjR6LcPU4D+X7Vo=", + "dev": true, + "requires": { + "async": "2.5.0", + "fileset": "2.0.3", + "istanbul-lib-coverage": "1.1.1", + "istanbul-lib-hook": "1.0.7", + "istanbul-lib-instrument": "1.7.5", + "istanbul-lib-report": "1.1.1", + "istanbul-lib-source-maps": "1.2.1", + "istanbul-reports": "1.1.2", + "js-yaml": "3.9.1", + "mkdirp": "0.5.1", + "once": "1.4.0" + }, + "dependencies": { + "async": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", + "dev": true, + "requires": { + "lodash": "4.17.4" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "js-yaml": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.1.tgz", + "integrity": "sha512-CbcG379L1e+mWBnLvHWWeLs8GyV/EMw862uLI3c+GxVyDHWZcjZinwuBd3iW2pgxgIlksW/1vNJa4to+RvDOww==", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + } + } + }, + "istanbul-lib-coverage": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", + "integrity": "sha512-0+1vDkmzxqJIn5rcoEqapSB4DmPxE31EtI2dF2aCkV5esN9EWHxZ0dwgDClivMXJqE7zaYQxq30hj5L0nlTN5Q==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz", + "integrity": "sha512-3U2HB9y1ZV9UmFlE12Fx+nPtFqIymzrqCksrXujm3NVbAZIJg/RfYgO1XiIa0mbmxTjWpVEVlkIZJ25xVIAfkQ==", + "dev": true, + "requires": { + "append-transform": "0.4.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.5.tgz", + "integrity": "sha1-rbWW+PDLi5XnOSBjUaOKWGryGx4=", + "dev": true, + "requires": { + "babel-generator": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.1.1", + "semver": "5.4.1" + } + }, + "istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-tvF+YmCmH4thnez6JFX06ujIA19WPa9YUiwjc1uALF2cv5dmE3It8b5I8Ob7FHJ70H9Y5yF+TDkVa/mcADuw1Q==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" + }, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz", + "integrity": "sha512-mukVvSXCn9JQvdJl8wP/iPhqig0MRtuWuD4ZNKo6vB2Ik//AmhAKe3QnPN02dmkRe3lTudFk3rzoHhwU4hb94w==", + "dev": true, + "requires": { + "debug": "2.6.8", + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "rimraf": "2.6.1", + "source-map": "0.5.7" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha1-D7Lj9qqZIr085F0F2KtNXo4HvU8=", + "dev": true, + "requires": { + "handlebars": "4.0.10" + } + }, + "items": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/items/-/items-2.1.1.tgz", + "integrity": "sha1-i9FtnIOxlSneWuoyGsqtp4NkoZg=" + }, + "joi": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-10.6.0.tgz", + "integrity": "sha512-hBF3LcqyAid+9X/pwg+eXjD2QBZI5eXnBFJYaAkH4SK3mp9QSRiiQnDYlmlz5pccMvnLcJRS4whhDOTCkmsAdQ==", + "requires": { + "hoek": "4.2.0", + "isemail": "2.2.1", + "items": "2.1.1", + "topo": "2.0.2" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz", + "integrity": "sha1-bl/mfYsgXOTSL60Ft3geja3MSzA=", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "2.7.3" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + }, + "kindof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/kindof/-/kindof-1.0.0.tgz", + "integrity": "sha1-ExiZqFJ1N6lNou3NXMSfxglgZWA=", + "dev": true + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "optional": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "lcov-parse": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", + "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=", + "dev": true + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash.keys": "3.1.2" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "dev": true, + "requires": { + "lodash._baseassign": "3.2.0", + "lodash._basecreate": "3.0.3", + "lodash._isiterateecall": "3.0.9" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "log-driver": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.5.tgz", + "integrity": "sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY=", + "dev": true + }, + "lolex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, + "make-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", + "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=", + "dev": true + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", + "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", + "dev": true, + "requires": { + "mime-db": "1.29.0" + }, + "dependencies": { + "mime-db": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz", + "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=", + "dev": true + } + } + }, + "mimos": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/mimos/-/mimos-3.0.3.tgz", + "integrity": "sha1-uRCQcq03jCty9qAQHEPd+ys2ZB8=", + "requires": { + "hoek": "4.2.0", + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mocha": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz", + "integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "mocha-typescript": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/mocha-typescript/-/mocha-typescript-1.1.7.tgz", + "integrity": "sha512-lry2h3fpqkLzi4y6cOSiyG5DSVL95l99s0GRmvg6BSzYHucnN9ZCAjM0G095tZA3StMo2hC1IgCigjeDN7NCKw==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "yargs": "6.6.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + } + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dev": true, + "requires": { + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "4.2.1" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "must": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/must/-/must-0.12.0.tgz", + "integrity": "sha1-V0HnKb7byLD+K8jFAGRZQVOD0F0=", + "dev": true, + "requires": { + "kindof": "1.0.0" + } + }, + "nan": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=" + }, + "nigel": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/nigel/-/nigel-2.0.2.tgz", + "integrity": "sha1-k6GGb7DFLYc5CqdeKxYfS1x15bE=", + "requires": { + "hoek": "4.2.0", + "vise": "2.0.2" + } + }, + "noder.io": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/noder.io/-/noder.io-1.2.0.tgz", + "integrity": "sha1-rSvGxsP5RliR7bxtv16E3K4vqeY=", + "dev": true + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1.0.9" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.4.1", + "validate-npm-package-license": "3.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "0.0.10", + "wordwrap": "0.0.3" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "1.3.1" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "pez": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/pez/-/pez-2.1.5.tgz", + "integrity": "sha1-XsLMYlAMw+tCNtSkFM9aF7XrUAc=", + "requires": { + "b64": "3.0.3", + "boom": "5.2.0", + "content": "3.0.5", + "hoek": "4.2.0", + "nigel": "2.0.2" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "podium": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/podium/-/podium-1.3.0.tgz", + "integrity": "sha512-ZIujqk1pv8bRZNVxwwwq0BhXilZ2udycQT3Kp8ah3f3TcTmVg7ILJsv/oLf47gRa2qeiP584lNq+pfvS9U3aow==", + "requires": { + "hoek": "4.2.0", + "items": "2.1.1", + "joi": "10.6.0" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", + "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "readable-stream": { + "version": "1.0.27-1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz", + "integrity": "sha1-a2eYPCA1fO/QfwFlABoW1xDZEHg=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "reduce-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz", + "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=", + "dev": true + }, + "reflect-metadata": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.10.tgz", + "integrity": "sha1-tPg3BEFqytiZiMmxVjXUfgO5NEo=" + }, + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "1.0.2" + } + }, + "request": { + "version": "2.79.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.16", + "oauth-sign": "0.8.2", + "qs": "6.3.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.4.3", + "uuid": "3.1.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", + "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4" + } + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "rxjs": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz", + "integrity": "sha512-fSNi+y+P9ss+EZuV0GcIIqPUK07DEaMRUtLJvdcvMyFjc9dizuDjere+A4V7JrLGnm9iCc+nagV/4QdMTkqC4A==", + "dev": true, + "requires": { + "symbol-observable": "1.0.4" + } + }, + "samsam": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", + "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "shot": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/shot/-/shot-3.4.2.tgz", + "integrity": "sha1-Hlw/bysmZJrcQvfrNQIUpaApHWc=", + "requires": { + "hoek": "4.2.0", + "joi": "10.6.0" + } + }, + "should": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/should/-/should-6.0.3.tgz", + "integrity": "sha1-2u4weGpVdmL7x3TACNfFh5Htsdk=", + "dev": true, + "requires": { + "should-equal": "0.3.1", + "should-format": "0.0.7", + "should-type": "0.0.4" + } + }, + "should-equal": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-0.3.1.tgz", + "integrity": "sha1-vY6pemdI45+tR2o75v1y68LnK/A=", + "dev": true, + "requires": { + "should-type": "0.0.4" + } + }, + "should-format": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-0.0.7.tgz", + "integrity": "sha1-Hi74a9kdqcLgQSM1tWq6vZov3hI=", + "dev": true, + "requires": { + "should-type": "0.0.4" + } + }, + "should-type": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-0.0.4.tgz", + "integrity": "sha1-ATKgVBemEmhmQmrPEW8e1WI6XNA=", + "dev": true + }, + "sinon": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", + "integrity": "sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=", + "dev": true, + "requires": { + "formatio": "1.1.1", + "lolex": "1.3.2", + "samsam": "1.1.2", + "util": "0.10.3" + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.16.3" + }, + "dependencies": { + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.16.tgz", + "integrity": "sha512-A6vlydY7H/ljr4L2UOhDSajQdZQ6dMD7cLH0pzwcmwLyc9u8PNI4WGtnfDDzX7uzGL6c/T+ORL97Zlh+S4iOrg==", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true, + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "statehood": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/statehood/-/statehood-5.0.3.tgz", + "integrity": "sha512-YrPrCt10t3ImH/JMO5szSwX7sCm8HoqVl3VFLOa9EZ1g/qJx/ZmMhN+2uzPPB/vaU6hpkJpXxcBWsgIkkG+MXA==", + "requires": { + "boom": "5.2.0", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "iron": "4.0.5", + "items": "2.1.1", + "joi": "10.6.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "subtext": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/subtext/-/subtext-5.0.0.tgz", + "integrity": "sha512-2nXG1G1V+K64Z20cQII7k0s38J2DSycMXBLMAk9RXUFG0uAkAbLSVoa88croX9VhTdBCJbLAe9g6LmzKwpJhhQ==", + "requires": { + "boom": "5.2.0", + "content": "3.0.5", + "hoek": "4.2.0", + "pez": "2.1.5", + "wreck": "12.2.3" + } + }, + "superagent": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-0.21.0.tgz", + "integrity": "sha1-+xUCeYR1HucVIgDmzSHNbhml3oc=", + "dev": true, + "requires": { + "component-emitter": "1.1.2", + "cookiejar": "2.0.1", + "debug": "2.6.8", + "extend": "1.2.1", + "form-data": "0.1.3", + "formidable": "1.0.14", + "methods": "1.0.1", + "mime": "1.2.11", + "qs": "1.2.0", + "readable-stream": "1.0.27-1", + "reduce-component": "1.0.1" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "combined-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", + "dev": true, + "requires": { + "delayed-stream": "0.0.5" + } + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "delayed-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", + "dev": true + }, + "extend": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", + "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=", + "dev": true + }, + "form-data": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.3.tgz", + "integrity": "sha1-TuQ0bm61Ni6DRKAgdb2NvYxzc+o=", + "dev": true, + "requires": { + "async": "0.9.2", + "combined-stream": "0.0.7", + "mime": "1.2.11" + } + }, + "methods": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.0.1.tgz", + "integrity": "sha1-dbyRlD3/19oDfPPusO1zoAN80Us=", + "dev": true + }, + "qs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.0.tgz", + "integrity": "sha1-7Qeb4oaCFH5v2aNMwrDB4OxkU+4=", + "dev": true + } + } + }, + "supertest": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-0.15.0.tgz", + "integrity": "sha1-hhGGld5L5Yhps+6UxF4dCEyn+sU=", + "dev": true, + "requires": { + "methods": "1.1.2", + "superagent": "0.21.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "symbol-observable": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", + "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "topo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz", + "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=", + "requires": { + "hoek": "4.2.0" + } + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "ts-node": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.3.0.tgz", + "integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=", + "dev": true, + "requires": { + "arrify": "1.0.1", + "chalk": "2.1.0", + "diff": "3.2.0", + "make-error": "1.3.0", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.4.16", + "tsconfig": "6.0.0", + "v8flags": "3.0.0", + "yn": "2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", + "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.2.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "supports-color": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.1.tgz", + "integrity": "sha512-qxzYsob3yv6U+xMzPrv170y8AwGP7i74g+pbixCfD6rgso8BscLT2qXIuz6TpOaiJZ3mFgT5O9lyT9nMU4LfaA==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "tsconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz", + "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", + "dev": true, + "requires": { + "strip-bom": "3.0.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "tslib": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.7.1.tgz", + "integrity": "sha1-vIAEFkaRkjp5/oN4u+s9ogF1OOw=", + "dev": true + }, + "tslint": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.7.0.tgz", + "integrity": "sha1-wl4NDJL6EgHCvDDoROCOaCtPNVI=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "colors": "1.1.2", + "commander": "2.11.0", + "diff": "3.2.0", + "glob": "7.1.2", + "minimatch": "3.0.4", + "resolve": "1.4.0", + "semver": "5.4.1", + "tslib": "1.7.1", + "tsutils": "2.8.1" + } + }, + "tsutils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.8.1.tgz", + "integrity": "sha1-N3FATnyp8L7fXZGaR6SxiQpo7/8=", + "dev": true, + "requires": { + "tslib": "1.7.1" + } + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "typedarray-to-buffer": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.2.tgz", + "integrity": "sha1-EBezLZhP9VbroQD1AViauhrOLgQ=", + "requires": { + "is-typedarray": "1.0.0" + } + }, + "typescript": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.1.tgz", + "integrity": "sha1-znzJOto94ZR1zJ0X463qeu4YMqo=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "optional": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "unit.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unit.js/-/unit.js-2.0.0.tgz", + "integrity": "sha1-6RtWq8UMUw26A82/4OHfKj7jqjI=", + "dev": true, + "requires": { + "bluebird": "2.11.0", + "lodash": "3.10.1", + "must": "0.12.0", + "noder.io": "1.2.0", + "should": "6.0.3", + "sinon": "1.17.7", + "supertest": "0.15.0" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + } + } + }, + "universalify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", + "dev": true + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + }, + "v8flags": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.0.tgz", + "integrity": "sha512-AGl+C+4qpeSu2g3JxCD/mGFFOs/vVZ3XREkD3ibQXEqr4Y4zgIrPWW124/IKJFHOIVFIoH8miWrLf0o84HYjwA==", + "dev": true, + "requires": { + "user-home": "1.1.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true, + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "vise": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/vise/-/vise-2.0.2.tgz", + "integrity": "sha1-awjo+0y3bjpQzW3Q7DczjoEaDTk=", + "requires": { + "hoek": "4.2.0" + } + }, + "websocket": { + "version": "1.0.24", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.24.tgz", + "integrity": "sha1-dJA+dfJUW2suHeFCW8HJBZF6GJA=", + "requires": { + "debug": "2.6.8", + "nan": "2.6.2", + "typedarray-to-buffer": "3.1.2", + "yaeti": "0.0.6" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true, + "optional": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "wreck": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/wreck/-/wreck-12.2.3.tgz", + "integrity": "sha512-6gIqKsW7Qc5s6cxLImozB0jUZ0N0x3Xycc8o4gMUzIINNOMGfmK1Fz532mrtrLS1XrLsfBxffNqkCj0UDJsigQ==", + "requires": { + "boom": "5.2.0", + "hoek": "4.2.0" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dev": true, + "requires": { + "camelcase": "3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + } + } + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + } + } +} diff --git a/package.json b/package.json index bed0b5d..6876faa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hapiness/core", - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "description": "Project to have a HapiJS (https://hapijs.com/) based framework to create easier NodeJS back-end with some awesome features", "main": "index.js", "types": "index.d.ts", @@ -72,35 +72,34 @@ }, "homepage": "https://github.com/hapinessjs/hapiness#readme", "dependencies": { - "@types/boom": "^4.3.5", - "@types/hapi": "^16.1.6", + "@types/hapi": "^16.1.9", "@types/hoek": "^4.1.3", - "@types/joi": "^10.4.0", - "@types/node": "^8.0.13", + "@types/joi": "^10.4.1", + "@types/node": "^8.0.25", "@types/websocket": "^0.0.34", - "debug": "^2.6.8", - "hapi": "^16.4.3", + "debug": "^3.0.1", + "hapi": "^16.5.2", "reflect-metadata": "^0.1.10", "websocket": "^1.0.24" }, "devDependencies": { - "@types/fs-extra": "^3.0.3", + "@types/fs-extra": "^4.0.0", "coveralls": "^2.13.1", - "fs-extra": "^4.0.0", + "fs-extra": "^4.0.1", "istanbul": "^1.1.0-alpha.1", - "mocha": "^3.4.2", + "mocha": "^3.5.0", "mocha-typescript": "^1.1.7", "rimraf": "^2.6.1", - "rxjs": "^5.4.2", - "ts-node": "^3.2.0", + "rxjs": "^5.4.3", + "ts-node": "^3.3.0", "tslint": "^5.5.0", - "typescript": "^2.4.1", + "typescript": "^2.5.1", "unit.js": "^2.0.0" }, "engines": { "node": ">=7.0.0" }, "peerDependencies": { - "rxjs": "^5.4.2" + "rxjs": "^5.4.3" } } diff --git a/src/core/bootstrap.ts b/src/core/bootstrap.ts deleted file mode 100644 index ba57211..0000000 --- a/src/core/bootstrap.ts +++ /dev/null @@ -1,135 +0,0 @@ -import 'rxjs/add/observable/forkJoin'; -import { Type } from './decorators'; -import { ExtentionHooksEnum, ModuleEnum } from './enums'; -import { HookManager } from './hook'; -import { CoreModule, CoreProvide, ModuleManager } from './module'; -import * as Hoek from 'hoek'; -import { Observable } from 'rxjs/Rx'; -const debug = require('debug')('hapiness:bootstrap'); - -export interface ExtensionWithConfig { - token: Type; - config: any; -} - -export interface Extension { - value: any; - instance: any; - token: Type; -} - -/** - * OnExtensionLoad Hook - * - * @param {CoreModule} module - * @param {any} config - * @returns Observable - */ -export interface OnExtensionLoad { onExtensionLoad(module: CoreModule, config: any): Observable } - -/** - * OnModuleInstantiated Hook - * - * @param {CoreModule} module - * @returns Observable - */ -export interface OnModuleInstantiated { onModuleInstantiated(module: CoreModule): Observable } - -export class Hapiness { - - private static module: CoreModule; - - private static extensions: Extension[]; - - /** - * Entrypoint to bootstrap a module - * will load the extentions and trigger - * module's hooks - * - * @param {Type} module - * @param {Array|ExtensionWithConfig>} extensions? - * @returns Promise - */ - public static bootstrap(module: Type, extensions?: Array | ExtensionWithConfig>): Promise<{}> { - return new Promise((resolve, reject) => { - Hoek.assert(!!module, 'Please provide a module to bootstrap'); - Hoek.assert(typeof module === 'function', 'Wrong module to bootstrap'); - debug(`bootstrapping ${module.name}`); - this.module = ModuleManager.resolveModule(module); - let errors = []; - const extensionsObs = (extensions || []) - .map(ext => this.toExtensionWithConfig(ext)) - .map(ext => this.loadExtention(ext)) - .map(ext => ext.catch(e => errors = errors.concat(e))); - let _extensions = []; - Observable.merge(...extensionsObs).subscribe(result => { - _extensions = [].concat(_extensions, result).filter(_ => !(_ instanceof Error)); - }, /* istanbul ignore next */ _ => reject(_), () => { - this.extensions = [].concat(_extensions).filter(_ => !!_); - if (errors.length) { - return reject(errors.shift()); - } - const providers = this.extensions.map(ext => { - return { provide: ext.token, useValue: ext.value }; - }); - ModuleManager.instantiateModule(this.module, providers).subscribe(instance => { - this.module = instance; - Observable.forkJoin( - ModuleManager.getModules(this.module) - .filter(m => !!m.parent) - .filter(m => HookManager.hasLifecycleHook(ModuleEnum.OnRegister.toString(), m.token)) - .map(m => HookManager.triggerHook(ModuleEnum.OnRegister.toString(), m.token, m.instance)) - .concat(this.extensions.map(ext => this.moduleInstantiated(ext))) - .concat(Observable.of('')) - ) - .switchMap(_ => HookManager.triggerHook(ModuleEnum.OnStart.toString(), this.module.token, - this.module.instance, null, false)) - .subscribe(_ => resolve(), _ => reject(_)); - }, /* istanbul ignore next */ _ => reject(_)); - }); - }); - } - - /** - * Convert an extension type to ExtensionWithConfig - * - * @param {} extension - * @returns ExtensionWithConfig - */ - private static toExtensionWithConfig(extension): ExtensionWithConfig { - if (extension && extension.token) { - return extension; - } - return { - token: >extension, - config: {} - }; - } - - /** - * Call the OnExtensionLoad hook - * of an extension - * - * @param {ExtensionWithConfig} extension - * @returns Observable - */ - private static loadExtention(extension: ExtensionWithConfig): Observable { - debug(`loading ${extension.token.name}`); - const instance = Reflect.construct(extension.token, []); - return HookManager.triggerHook(ExtentionHooksEnum.OnExtensionLoad.toString(), - extension.token, instance, [ this.module, extension.config ]); - } - - /** - * Call the OnModuleInstantiated hook - * of an extension - * - * @param {Extension} extension - * @returns Observable - */ - private static moduleInstantiated(extension: Extension): Observable { - debug('moduleInstantiated', extension.token.name); - return HookManager.triggerHook(ExtentionHooksEnum.OnModuleInstantiated.toString(), - extension.token, extension.instance, [ this.module ]); - } -} diff --git a/src/core/di.ts b/src/core/di.ts index 5b32933..9ef2063 100644 --- a/src/core/di.ts +++ b/src/core/di.ts @@ -1,24 +1,38 @@ import { ReflectiveInjector } from '../externals/injection-js'; import { Type } from '../externals/injection-js/facade/type'; -import { ReflectiveDependency, ResolvedReflectiveProvider, ResolvedReflectiveFactory } from '../externals/injection-js/reflective_provider'; +import { + ReflectiveDependency, + ResolvedReflectiveProvider, + ResolvedReflectiveFactory +} from '../externals/injection-js/reflective_provider'; +import { CoreProvide } from './interfaces'; +import { InternalLogger } from './logger'; +import { Observable } from 'rxjs'; export class DependencyInjection { + private static logger = new InternalLogger('di'); + /** * Create a new DI and * can inherits from a parent DI * * @param {Type[]} providers * @param {ReflectiveInjector} parent? - * @returns ReflectiveInjector + * @returns Observable */ - static createAndResolve(providers: Type[], parent?: ReflectiveInjector): ReflectiveInjector { - return parent ? parent.resolveAndCreateChild(providers) - : ReflectiveInjector.resolveAndCreate(providers); + static createAndResolve(providers: Type[] | CoreProvide[], parent?: ReflectiveInjector): Observable { + return Observable + .of(parent) + .map(_ => !!_ ? + parent.resolveAndCreateChild(providers) : + ReflectiveInjector.resolveAndCreate(providers) + ) + .do(_ => this.logger.debug(`DI created, providers: ${providers.length}`)); } /** - * Instanciate a component + * Instantiate a component * resolving its dependencies * without inject the component * into the DI @@ -27,16 +41,15 @@ export class DependencyInjection { * @param {ReflectiveInjector} di * @returns T */ - static instantiateComponent(component: Type, di: ReflectiveInjector): T { - try { - const reflectiveDeps: ReflectiveDependency[] = ReflectiveInjector.resolve([component]) - .reduce((a, x: ResolvedReflectiveProvider) => a.concat(x.resolvedFactories), []) - .reduce((a, r: ResolvedReflectiveFactory) => a.concat(r.dependencies), []); - const deps = reflectiveDeps.map(d => di['_getByReflectiveDependency'](d)); - return Reflect.construct(component, deps); - } catch (e) { - console.error.apply(console, e); - } + static instantiateComponent(component: Type, di: ReflectiveInjector): Observable { + return Observable + .from(ReflectiveInjector.resolve([component])) + .reduce((a, x: ResolvedReflectiveProvider) => a.concat(x.resolvedFactories), []) + .map(_ => _.reduce((a, r: ResolvedReflectiveFactory) => a.concat(r.dependencies), [])) + .map(_ => _.filter(__ => !!__)) + .do(_ => this.logger.debug(`Component '${component.name}' deps: ${_.length}`)) + .map(_ => _.map(d => di['_getByReflectiveDependency'](d))) + .map(_ => Reflect.construct(component, _)); } } diff --git a/src/core/enums.ts b/src/core/enums.ts index 2ae568a..c64430d 100644 --- a/src/core/enums.ts +++ b/src/core/enums.ts @@ -8,3 +8,13 @@ export enum ModuleEnum { OnError = 'onError', OnRegister = 'onRegister' } + +/** + * Represents the position where + * the module is instantiate + */ +export enum ModuleLevel { + ROOT, + PRIMARY, + SECONDARY +} diff --git a/src/core/hapiness.ts b/src/core/hapiness.ts new file mode 100644 index 0000000..e387400 --- /dev/null +++ b/src/core/hapiness.ts @@ -0,0 +1,225 @@ +import { Observable } from 'rxjs'; +import { CoreModule, Extension, ExtensionWithConfig } from './interfaces'; +import { InternalLogger } from './logger'; +import { Type } from './decorators'; +import { ExtentionHooksEnum, ModuleEnum, ModuleLevel } from './enums'; +import { ModuleManager } from './module'; +import { HookManager } from './hook'; + +export class Hapiness { + + private static module: CoreModule; + private static extensions: Extension[]; + private static logger = new InternalLogger('bootstrap'); + + /** + * Entrypoint to bootstrap a module + * will load the extentions and trigger + * module's hooks + * + * @param {Type} module + * @param {Array|ExtensionWithConfig>} extensions? + * @returns Promise + */ + public static bootstrap(module: Type, extensions?: Array | ExtensionWithConfig>): Promise { + return new Promise((resolve, reject) => { + this + .checkArg(module) + .flatMap(_ => ModuleManager.resolve(_)) + .flatMap(_ => this.loadExtensions(extensions, _)) + .ignoreElements() + .subscribe( + null, + _ => reject(_), + () => resolve() + ) + }); + } + + /** + * Load extensions + * + * @param {Array|ExtensionWithConfig>} extensions + * @param {CoreModule} moduleResolved + * @returns Observable + */ + private static loadExtensions(extensions: Array | ExtensionWithConfig>, moduleResolved: CoreModule): Observable { + return Observable + .from([].concat(extensions).filter(_ => !!_)) + .map(_ => this.toExtensionWithConfig(_)) + .flatMap(_ => this.loadExtention(_, moduleResolved)) + .toArray() + .flatMap(_ => this.instantiateModule(_, moduleResolved)); + } + + /** + * Instantiate module + * + * @param {Extension[]} extensionsLoaded + * @param {CoreModule} moduleResolved + * @returns Observable + */ + private static instantiateModule(extensionsLoaded: Extension[], moduleResolved: CoreModule): Observable { + return Observable + .from(extensionsLoaded) + .map(_ => ({ provide: _.token, useValue: _.value })) + .toArray() + .flatMap(_ => ModuleManager.instantiate(moduleResolved, _)) + .flatMap(_ => this.callRegister(_)) + .do(_ => this.extensions = extensionsLoaded) + .flatMap(moduleInstantiated => + Observable + .from(extensionsLoaded) + .flatMap(_ => this.moduleInstantiated(_, moduleInstantiated)) + .toArray() + .map(_ => moduleInstantiated) + ) + .do(_ => this.module = _) + .flatMap(_ => this.callStart(_)); + } + + /** + * Call Register Hooks + * + * @param {CoreModule} moduleInstantiated + * @returns Observable + */ + private static callRegister(moduleInstantiated: CoreModule): Observable { + return Observable + .from(ModuleManager.getModules(moduleInstantiated)) + .filter(_ => _.level !== ModuleLevel.ROOT) + .filter(_ => HookManager + .hasLifecycleHook(ModuleEnum.OnRegister.toString(), _.token) + ) + .flatMap(_ => HookManager + .triggerHook(ModuleEnum.OnRegister.toString(), _.token, _.instance) + ) + .toArray() + .map(_ => moduleInstantiated) + } + + /** + * Call Start Hooks + * + * @param {CoreModule} moduleInstantiated + * @returns Observable + */ + private static callStart(moduleInstantiated: CoreModule): Observable { + return Observable + .of(moduleInstantiated) + .flatMap(_ => HookManager + .triggerHook( + ModuleEnum.OnStart.toString(), + moduleInstantiated.token, + moduleInstantiated.instance, + null, + false + ) + ); + } + + /** + * Check if the provided module + * is right + * + * @param {Type} module + * @returns Observable + */ + private static checkArg(module: Type): Observable> { + return Observable + .of(module) + .do(_ => this.module = null) + .do(_ => this.extensions = null) + .flatMap(_ => !!_ ? + Observable.of(_) : + Observable.throw(new Error('Bootstrap failed: no module provided')) + ) + .flatMap(_ => typeof _ === 'function' ? + Observable.of(_) : + Observable.throw(new Error('Bootstrap failed: module must be a function/class')) + ); + } + + /** + * Convert an extension type to ExtensionWithConfig + * + * @param {} extension + * @returns ExtensionWithConfig + */ + private static toExtensionWithConfig(extension): ExtensionWithConfig { + if (extension && extension.token) { + return extension; + } + return { + token: >extension, + config: {} + }; + } + + /** + * Call the OnExtensionLoad hook + * of an extension + * + * @param {ExtensionWithConfig} extension + * @returns Observable + */ + private static loadExtention(extension: ExtensionWithConfig, module: CoreModule): Observable { + return Observable + .of(Reflect.construct(extension.token, [])) + .do(_ => this.logger.debug(`loading ${extension.token.name}`)) + .switchMap(instance => + HookManager + .triggerHook( + ExtentionHooksEnum.OnExtensionLoad.toString(), + extension.token, + instance, + [ module, extension.config ] + ) + ); + } + + /** + * Call the OnModuleInstantiated hook + * of an extension + * + * @param {Extension} extension + * @returns Observable + */ + private static moduleInstantiated(extension: Extension, module: CoreModule): Observable { + return HookManager + .triggerHook( + ExtentionHooksEnum.OnModuleInstantiated.toString(), + extension.token, + extension.instance, + [ module, extension.value ] + ) + .do(_ => this.logger.debug(`moduleInstantiated ${extension.token.name}`)) + .defaultIfEmpty(null); + } + +} + +/** + * @param {Error} error + * @returns void + */ +export function errorHandler(error: Error): void { + Observable + .of(Hapiness['module']) + .filter(_ => !!(_ && _.instance)) + .flatMap(_ => + HookManager + .hasLifecycleHook(ModuleEnum.OnError.toString(), _.token) ? + HookManager + .triggerHook( + ModuleEnum.OnError.toString(), + _.token, + _.instance, + [ error ], + false + ) : + Observable + .throw(error) + ) + .subscribe(null, _ => console.error(_)); +} diff --git a/src/core/hook.ts b/src/core/hook.ts index 19791b0..bdcf0ae 100644 --- a/src/core/hook.ts +++ b/src/core/hook.ts @@ -1,58 +1,53 @@ import { reflector } from '../externals/injection-js/reflection/reflection'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Type } from './decorators'; -import * as Hoek from 'hoek'; -const debug = require('debug')('hapiness:hook'); +import { InternalLogger } from './logger'; export class HookManager { - /** - * Check if a token has a hook implemented - * - * @param {string} hook - * @param {Type} token - * @returns boolean - */ - public static hasLifecycleHook(hook: string, token: Type): boolean { - debug('checking hook', hook, token ? token.name : null); - return reflector.hasLifecycleHook(token, hook); - } + private static logger = new InternalLogger('hook'); - /** - * Trigger the hook if - * it is implemented - * - * @param {string} hook - * @param {Type} token - * @param {T} instance - * @param {any[]} args - * @returns Observable - */ - public static triggerHook(hook: string, token: Type, instance: T, args?: any[], throwErr?: boolean): Observable { - debug('triggering hook', hook, token ? token.name : null); - Hoek.assert((!!token && !!instance), 'Cannot trigger without token/instance'); - if (this.hasLifecycleHook(hook, token)) { - try { - const result = Reflect.apply(instance[hook], instance, args || []); - if (result instanceof Observable) { - return result; - } else { - return Observable.create(observer => { - observer.next(result); - observer.complete(); - }); - } - } catch (e) { - console.error.apply(console, e); - } + /** + * Check if a token has a hook implemented + * + * @param {string} hook + * @param {Type} token + * @returns boolean + */ + public static hasLifecycleHook(hook: string, token: Type): boolean { + return reflector.hasLifecycleHook(token, hook); + } + + /** + * Trigger the hook if + * it is implemented + * + * @param {string} hook + * @param {Type} token + * @param {T} instance + * @param {any[]} args + * @param {boolean} throwErr + * @returns Observable + */ + public static triggerHook(hook: string, token: Type, instance: T, args?: any[], throwErr?: boolean): Observable { + return Observable + .merge( + Observable + .of(this.hasLifecycleHook(hook, token)) + .filter(_ => !!_) + .map(_ => Reflect.apply(instance[hook], instance, args || [])) + .flatMap(_ => + (_ instanceof Observable) ? + _ : !!_ ? + Observable.of(_) : + Observable.empty() + ), + + Observable + .of(this.hasLifecycleHook(hook, token)) + .filter(_ => !_ && throwErr) + .flatMap(_ => Observable.throw(new Error(`Hook missing ${hook} on ${token.name}`))) + ) + .do(_ => this.logger.debug(`Triggering hook '${hook}' on '${token.name}'`)); } - return Observable.create((observer) => { - if (throwErr) { - observer.error(new Error(`Hook missing ${hook} on ${token.name}`)); - } else { - observer.next(); - } - observer.complete(); - }); - } } diff --git a/src/core/index.ts b/src/core/index.ts index b63d18d..42aaccc 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,7 +1,9 @@ -export * from './bootstrap'; +export * from './hapiness'; export * from './decorators'; export * from './di'; export * from './enums'; export * from './hook'; export * from './metadata'; export * from './module'; +export * from './interfaces'; +export * from './logger'; diff --git a/src/core/interfaces/extension.ts b/src/core/interfaces/extension.ts new file mode 100644 index 0000000..83716e9 --- /dev/null +++ b/src/core/interfaces/extension.ts @@ -0,0 +1,31 @@ +import { Type } from '../decorators'; +import { Observable } from 'rxjs'; +import { CoreModule } from './module'; + +export interface ExtensionWithConfig { + token: Type; + config: any; +} + +export interface Extension { + value: any; + instance: any; + token: Type; +} + +/** + * OnExtensionLoad Hook + * + * @param {CoreModule} module + * @param {any} config + * @returns Observable + */ +export interface OnExtensionLoad { onExtensionLoad(module: CoreModule, config: any): Observable } + +/** + * OnModuleInstantiated Hook + * + * @param {CoreModule} module + * @returns Observable + */ +export interface OnModuleInstantiated { onModuleInstantiated(module: CoreModule, server: any): Observable } diff --git a/src/core/interfaces/index.ts b/src/core/interfaces/index.ts new file mode 100644 index 0000000..90a1e66 --- /dev/null +++ b/src/core/interfaces/index.ts @@ -0,0 +1,2 @@ +export * from './module'; +export * from './extension'; diff --git a/src/core/interfaces/module.ts b/src/core/interfaces/module.ts new file mode 100644 index 0000000..099c864 --- /dev/null +++ b/src/core/interfaces/module.ts @@ -0,0 +1,72 @@ +import { Type } from '../decorators'; +import { ReflectiveInjector } from '../../externals/injection-js'; +import { Observable } from 'rxjs'; +import { ModuleLevel } from '../enums'; + +/** + * CoreProvide Type + * Used by CoreModule Type + */ +export interface CoreProvide { + provide: any; + useClass?: any; + useValue?: any; + useExisting?: any; + useFactory?: any; + deps?: any[]; +} + +/** + * CoreModule Type + * Represents a Module + */ +export class CoreModule { + token: Type | any; + name: string; + version: string; + instance?: any; + level: ModuleLevel; + di?: ReflectiveInjector; + providers?: CoreProvide[]; + modules?: CoreModule[]; + parent?: CoreModule; + exports?: Type[] | any[]; + declarations?: Type[] | any[]; +} + +/** + * CoreModuleWithProviders Type + * Used to pass data while module importation + */ +export interface CoreModuleWithProviders { + module: Type; + providers: CoreProvide[]; +} + +/** + * Module Lifecycle Hook + * called once the module has been + * registered into the server + * + * @returns void | Observable + */ +export interface OnRegister { onRegister(): void | Observable; } + +/** + * Module Lifecycle Hook + * called once the server has started + * only for the MainModule + * + * @returns void | Observable + */ +export interface OnStart { onStart(): void | Observable; } + +/** + * Module Lifecycle Hook + * called when an error + * occured in components + * + * @returns void | Observable + */ +export interface OnError { onError(error: Error): void | Observable; } + diff --git a/src/core/logger.ts b/src/core/logger.ts new file mode 100644 index 0000000..f414ce5 --- /dev/null +++ b/src/core/logger.ts @@ -0,0 +1,17 @@ +import * as Debug from 'debug'; + +export class InternalLogger { + + private logger; + + /* istanbul ignore next */ + constructor(private tag = 'core', extension?: string) { + this.logger = Debug(extension ? `hapiness:${extension}` : 'hapiness'); + } + + /* istanbul ignore next */ + debug(message: string): void { + this.logger(`[${this.tag.toUpperCase()}] > ${message}`); + } + +} diff --git a/src/core/metadata.ts b/src/core/metadata.ts index 30f099a..5c7cb9b 100644 --- a/src/core/metadata.ts +++ b/src/core/metadata.ts @@ -42,5 +42,5 @@ export function extractMetadatas(decorator: any): any[] { .filter(x => x === 'annotations') .map(x => Reflect.getOwnMetadata(x, decorator)) .map(x => [].concat(x)) - .pop(); + .pop() || []; }; diff --git a/src/core/module.ts b/src/core/module.ts index 82a9c1b..aca3ed8 100644 --- a/src/core/module.ts +++ b/src/core/module.ts @@ -1,235 +1,245 @@ +import { Observable } from 'rxjs'; +import { InternalLogger } from './logger'; import { extractMetadataByDecorator } from './metadata'; -import { ReflectiveInjector } from '../externals/injection-js'; import { HapinessModule, Type, InjectionToken } from './decorators'; +import { CoreModule, CoreProvide, CoreModuleWithProviders } from './interfaces'; import { DependencyInjection } from './di'; -import * as Hoek from 'hoek'; -import { Observable } from 'rxjs/Rx'; -const debug = require('debug')('hapiness:module'); +import { ModuleLevel } from './enums'; -/** - * CoreProvide Type - * Used by CoreModule Type - */ -export interface CoreProvide { - provide: any; - useClass?: any; - useValue?: any; - useExisting?: any; - useFactory?: any; - deps?: any[]; -} - -/** - * CoreModule Type - * Represents a Module - */ -export class CoreModule { - token: Type | any; - name: string; - version: string; - instance?: any; - level: ModuleLevel; - di?: ReflectiveInjector; - providers?: CoreProvide[]; - modules?: CoreModule[]; - parent?: CoreModule; - exports?: Type[] | any[]; - declarations?: Type[] | any[]; -} - -/** - * CoreModuleWithProviders Type - * Used to pass data while module importation - */ -export interface CoreModuleWithProviders { - module: Type; - providers: CoreProvide[]; -} +export class ModuleManager { -/** - * Represents the position where - * the module is instantiate - */ -export enum ModuleLevel { - ROOT, - PRIMARY, - SECONDARY -} + private static decoratorName = 'HapinessModule'; -/** - * ModuleManage - * Class used to manage modules - */ -export class ModuleManager { + private static logger = new InternalLogger('module'); /** - * Helper to extract metadata - * @property {string} decoratorName + * Resolve into a tree of CoreModule + * + * @param {any} module + * @returns Observable */ - private static decoratorName = 'HapinessModule'; + static resolve(module: any): Observable { + this.logger.debug(`Resolving module '${module.name}'`); + return this.resolution(module); + } + + static instantiate(module: CoreModule, providers?: CoreProvide[]): Observable { + this.logger.debug(`Instantiation of module '${module.name}'`); + return this.instantiation(module, providers); + } /** - * Entrypoint to resolve a CoreModule - * Get the metadata. + * Get all the tree modules * - * @param {Type} module - * @param {CoreProvide[]} providers + * @param {CoreModule} module * @returns CoreModule */ - public static resolveModule(module: any): CoreModule { - debug('building module', module.name); - const moduleResolved = this.recursiveResolution(module, null); - debug('module resolved', module.name); - return moduleResolved; + static getModules(module: CoreModule): CoreModule[] { + const lookup = (_module: CoreModule) => { + return [] + .concat(_module) + .concat([] + .concat(_module.modules) + .filter(_ => !!_) + .map(m => lookup(m)) + .reduce((a, c) => a.concat(c), []) + ); + }; + return lookup(module); } - public static instantiateModule(module: CoreModule, providers?: CoreProvide[]): Observable { - debug('instantiate module', module.name, 'extra providers:', providers ? providers.length : 0); - return Observable.create(observer => { - try { - observer.next(this.recursiveInstantiation(module, null, providers)); - observer.complete(); - } catch (err) { - /* istanbul ignore next */ - observer.error(err); - /* istanbul ignore next */ - observer.complete(); - } - }); + /** + * Helper to convert provider + * to a CoreProvide type + * + * @param {any} provider + * @returns CoreProvide + */ + static toCoreProvider(provider: any): CoreProvide { + return (!!provider.provide ? + provider : + { provide: provider, useClass: provider } + ); } /** - * Lookup for module in the - * importation tree by its name + * =========================================================================== + * + * MODULE RESOLUTION * - * @param name - * @param module - * @returns CoreModule | null + * =========================================================================== */ - public static findNestedModule(name: string, module: CoreModule): CoreModule { - debug(`looking for nested module ${name} in ${module.name}`); - if (module.modules && module.modules.length > 0 && - module.modules.find(m => m.name === name)) { - debug(`found nested module ${name} in ${module.name}`); - return module.modules.find(m => m.name === name); - } else if (module.modules && module.modules.length > 0) { - debug(`looking in sub-modules`); - return module.modules - .map(m => this.findNestedModule(name, m)) - .filter(m => !!m) - .shift(); - } - debug(`didn't find module ${name}`); - } /** - * Get elements of a module + * Process module to CoreModule type + * from metadata and the children * - * @param {CoreModule} module - * @param {string} element - * @returns Type + * @param {any} module + * @param {CoreModule} parent? + * @returns Observable */ - public static getElements(module: CoreModule, element: string): Type[] { - Hoek.assert(!!element, 'You need to provide the element you want to get'); - const lookup = (_module: CoreModule) => { - const els = [].concat((_module[element] && Array.isArray(_module[element])) ? _module[element] : []); - return (_module.modules || []).map(m => lookup(m)).reduce((acc, cur) => acc.concat(cur), []).concat(els).filter(_ => !!_); - }; - return lookup(module); + private static resolution(module: any, parent?: CoreModule): Observable { + return Observable + .of(module) + .map(_ => this.toCoreModuleWithProviders(_)) + .flatMap(cmwp => + this + .extractMetadata(cmwp.module) + .map(_ => ({ metadata: _, moduleWithProviders: cmwp })) + ) + .flatMap(mcmwp => + this + .metadataToCoreModule(mcmwp.metadata, mcmwp.moduleWithProviders, parent) + .map(_ => this.coreModuleParentConfigProviders(_)) + .map(_ => Object.assign({ module: _ }, mcmwp)) + ) + .flatMap(data => + Observable + .from(data.metadata.imports || []) + .flatMap(_ => this.resolution(_, data.module)) + .toArray() + .do(_ => this.logger.debug(`'${data.module.name}' got ${_.length} children`)) + .map(_ => Object.assign({ modules: _ }, data.module)) + ) } /** - * Get all the tree modules + * FIX for exported providers + * that need internal config * + * @todo find a better solution * @param {CoreModule} module * @returns CoreModule */ - public static getModules(module: CoreModule): CoreModule[] { - const lookup = (_module: CoreModule) => { - return [].concat(_module).concat((_module.modules || []).map(m => lookup(m)).reduce((a, c) => a.concat(c), [])); - }; - return lookup(module); + private static coreModuleParentConfigProviders(module: CoreModule): CoreModule { + module.providers = [] + .concat(module.providers) + .concat((module.parent && module.parent.providers) ? + module.parent.providers.filter(_ => (_.provide instanceof InjectionToken)) : + [] + ) + .filter(_ => !!_); + return module; } /** - * Transform metadata to instance CoreModule - * - * @param {HapinessModule} data - * @param {Type} module - * @param {CoreModule} parent - * @returns CoreModule - */ - private static coreModuleFromMetadata(data: HapinessModule, module: CoreModuleWithProviders, parent?: CoreModule): CoreModule { - const providers = data.providers || []; - debug('converting module to CoreModule', module.module.name); - return { - parent, - token: module.module, - name: module.module.name, - version: data.version, - exports: data.exports, - declarations: data.declarations || [], - providers: providers.concat(module.providers).map((p: any) => !!p.provide ? - /* istanbul ignore next */ p : {provide: p, useClass: p}), - level: parent ? parent.level === ModuleLevel.ROOT ? - ModuleLevel.PRIMARY : ModuleLevel.SECONDARY : ModuleLevel.ROOT - }; + * Convert metadata to CoreModule type + * + * @param {HapinessModule} metadata + * @param {CoreModuleWithProviders} moduleWithProviders + * @param {CoreModule} parent? + * @returns Observable + */ + private static metadataToCoreModule( + metadata: HapinessModule, + moduleWithProviders: CoreModuleWithProviders, + parent?: CoreModule): Observable { + + return Observable + .of({ + parent, + token: moduleWithProviders.module, + name: moduleWithProviders.module.name, + version: metadata.version, + exports: metadata.exports || [], + declarations: metadata.declarations || [], + providers: (metadata.providers || []) + .concat(moduleWithProviders.providers) + .map(_ => this.toCoreProvider(_)), + level: !!parent ? + parent.level === ModuleLevel.ROOT ? + ModuleLevel.PRIMARY : + ModuleLevel.SECONDARY : + ModuleLevel.ROOT + }) + .do(_ => this.logger.debug(`Build CoreModule for '${_.name}'`)); } /** - * Extract metadata from - * the module provided + * Get HapinessModule metadata type + * if does not exist, throw an error * - * @todo Metadata interface matching... * @param {Type} module - * @returns HapinessModule + * @returns Observable */ - public static metadataFromModule(module: Type): HapinessModule { - debug('metadata for', module.name); - const metadata = extractMetadataByDecorator(module, this.decoratorName); - Hoek.assert(!!metadata, new Error('Please define a Module with the right annotation')); - return metadata; + private static extractMetadata(module: Type): Observable { + return Observable + .of(extractMetadataByDecorator(module, this.decoratorName)) + .flatMap(_ => !!_ ? + Observable.of(_) : + Observable.throw(new Error(`Module '${module ? module.name : null}' resolution failed: No metadata`)) + ) } /** - * Resolve recursively - * each module imported + * Make sure to convert module into + * a CoreModuleWithProviders type * - * @todo Fix circular importation !!! - * @param {Type|CoreModuleWithProviders} module - * @param {CoreModule} parent - * @param {CoreProvide[]} providers - * @returns CoreModule + * @param {CoreModuleWithProviders|Type} module + * @returns CoreModuleWithProviders */ - private static recursiveResolution(module: CoreModuleWithProviders, parent?: CoreModule): CoreModule { - if (!module['module']) { - module = { + private static toCoreModuleWithProviders(module: CoreModuleWithProviders | Type): CoreModuleWithProviders { + return ((module && module['module']) ? + module : + { module, providers: [] - } - } - debug('recursive resolution', module.module.name); - const metadata = this.metadataFromModule(module.module); - const coreModule = this.coreModuleFromMetadata(metadata, module, parent); - coreModule.providers = coreModule.providers.concat( - (parent && parent.providers) ? - parent.providers.filter(_ => (_.provide instanceof InjectionToken)) : - [] - ); - coreModule.modules = (metadata.imports && metadata.imports.length > 0) ? - metadata.imports.map(m => this.recursiveResolution(m, coreModule)) : []; - return coreModule; + }); } - private static recursiveInstantiation(module: CoreModule, parent?: CoreModule, providers?: CoreProvide[]): CoreModule { - debug('recursive instantiation', module.name); - if (module.modules && module.modules.length > 0) { - module.modules.forEach(m => this.recursiveInstantiation(m, module, providers)); - } - module.di = DependencyInjection.createAndResolve(this.collectProviders(module, providers)); - module.instance = DependencyInjection.instantiateComponent(module.token, module.di); - this.instantiateLibs(module); - return module; + /** + * =========================================================================== + * + * MODULE INSTANTIATION + * + * =========================================================================== + */ + + /** + * Create the module's DI + * and instantiate the module + * + * @param {CoreModule} module + * @param {CoreProvide[]} providers? + * @param {CoreModule} parent? + * @returns Observable + */ + private static instantiation(module: CoreModule, providers?: CoreProvide[], parent?: CoreModule): Observable { + return Observable + .of(module) + .flatMap(_ => + Observable + .from(_.modules) + .flatMap(child => this.instantiation(child, providers, parent)) + .toArray() + .map(children => Object.assign({}, _, { modules: children })) + ) + .flatMap(_ => + DependencyInjection + .createAndResolve(this.collectProviders(_, providers)) + .map(di => Object.assign({ di }, _)) + ) + .flatMap(_ => + DependencyInjection + .instantiateComponent(_.token, _.di) + .map(instance => Object.assign({ instance }, _)) + ) + .flatMap(_ => this.instantiateLibs(_)); + } + + /** + * Instantiate and return array of libs + * + * @param {CoreModule} module + * @returns Type + */ + private static instantiateLibs(module: CoreModule): Observable { + return Observable + .from(module.declarations) + .filter(_ => !!_ && !!extractMetadataByDecorator(_, 'Lib')) + .flatMap(_ => DependencyInjection.instantiateComponent(_, module.di)) + .toArray() + .map(_ => module); } /** @@ -238,60 +248,34 @@ export class ModuleManager { * * @param {HapinessModule} module */ - private static collectProviders(module: CoreModule, providers?: CoreProvide[]): any[] { - debug('collect providers'); - return [].concat(module.providers || []) + private static collectProviders(module: CoreModule, providers?: CoreProvide[]): CoreProvide[] { + this.logger.debug(`Collect providers for '${module.name}'`); + return [] + .concat(module.providers) .concat(providers) - .filter(x => !!x) + .filter(_ => !!_) .concat(this.extractExportedProviders(module)); } /** - * Extract exported providers + * Extract exported children providers * * @param {CoreModule} module - * @returns CoreProvide + * @returns CoreProvide[] */ private static extractExportedProviders(module: CoreModule): CoreProvide[] { - Hoek.assert(!!module, 'Provide a module'); - debug('exported providers extraction', module.name); - return (module.modules || []) + this.logger.debug(`Extract exported children providers for '${module.name}'`); + return [] + .concat(module.modules) .filter(_ => (!!_.exports && _.exports.length > 0)) - .map(_ => { - const exp = _.exports; - return exp.concat((_.providers || []).filter(__ => (__.provide instanceof InjectionToken))); - }) + .map(_ => [] + .concat(_.exports) + .concat( + _.providers + .filter(__ => (__.provide instanceof InjectionToken))) + ) .reduce((a, c) => a.concat(c), []) - .filter(_ => !!_); - } - - /** - * Instantiate and return array of libs - * - * @param {CoreModule} module - * @returns Type - */ - private static instantiateLibs(module: CoreModule): Type[] { - return [].concat(module.declarations).filter(decl => !!extractMetadataByDecorator(decl, 'Lib')) - .map(lib => >DependencyInjection.instantiateComponent(lib, module.di)); + .filter(_ => !!_) + .map(_ => this.toCoreProvider(_)); } } - -/** - * Module Lifecycle Hook - * called once the module has been - * registered into the server - * - * @returns void - */ -export interface OnRegister { onRegister(): void; } - -/** - * Module Lifecycle Hook - * called once the server has started - * only for the MainModule - * - * @returns void - */ -export interface OnStart { onStart(): void; } - diff --git a/src/extensions/http-server/decorators.ts b/src/extensions/http-server/decorators.ts index 28a2c8b..88bbb3a 100644 --- a/src/extensions/http-server/decorators.ts +++ b/src/extensions/http-server/decorators.ts @@ -1,5 +1,5 @@ import { createDecorator, CoreDecorator, Type } from '../../core/decorators'; -import { RouteConfig } from './route'; +import { RouteConfig } from './interfaces'; export interface Route { path: string; diff --git a/src/extensions/http-server/extension.ts b/src/extensions/http-server/extension.ts index ae0142c..283f313 100644 --- a/src/extensions/http-server/extension.ts +++ b/src/extensions/http-server/extension.ts @@ -1,24 +1,18 @@ -import { ExtensionWithConfig } from '../../core'; -import { Extension, OnExtensionLoad, OnModuleInstantiated } from '../../core/bootstrap'; +import { ModuleLevel } from '../../core/enums'; import { DependencyInjection } from '../../core/di'; import { HookManager } from '../../core/hook'; import { extractMetadataByDecorator } from '../../core/metadata'; -import { CoreModule, ModuleManager } from '../../core/module'; +import { ModuleManager } from '../../core/module'; +import { errorHandler } from '../../core/hapiness'; +import { CoreModule, OnExtensionLoad, OnModuleInstantiated, ExtensionWithConfig, Extension } from '../../core/interfaces'; import { Lifecycle } from './decorators'; +import { Type } from '../../core/decorators'; import { enumByMethod, LifecycleComponentEnum } from './enums'; import { LifecycleManager } from './lifecycle'; -import { CoreRoute, RouteBuilder } from './route'; -import { Observable } from 'rxjs/Observable'; -import { RouteConfiguration, Server } from 'hapi'; -import * as Boom from 'boom'; -import * as Hoek from 'hoek'; -import * as Debug from 'debug'; -const debug = Debug('hapiness:extension:httpserver'); - -export interface HapiConfig { - host: string; - port: number; -} +import { RouteBuilder } from './route'; +import { CoreRoute, HapiConfig } from './interfaces'; +import { Observable } from 'rxjs'; +import { RouteConfiguration, Server, Request, ReplyNoContinue, ReplyWithContinue } from 'hapi'; export class HttpServerExt implements OnExtensionLoad, OnModuleInstantiated { @@ -31,161 +25,152 @@ export class HttpServerExt implements OnExtensionLoad, OnModuleInstantiated { }; } - onExtensionLoad(module: CoreModule, config: HapiConfig): Observable { - this.server = new Server(); - const connection = this.server.connection(config); - debug('server instantiation'); - return Observable.create(observer => { - Observable.forkJoin( - this.registrationObservables(module, this.server, this.flattenModules(module)).concat(this.addRoutes(module, this.server)) - ).subscribe(routes => { - debug('routes and plugins registered'); - LifecycleManager.routeLifecycle(this.server, routes.reduce((a, c) => a.concat(c), [])); - observer.next({ - instance: this, - token: HttpServerExt, - value: this.server - }); - observer.complete(); - }, err => { - observer.error(err); - observer.complete(); - }); - }); - } - - onModuleInstantiated(module: CoreModule) { - return Observable.create(observer => { - this.instantiateLifecycle(this.server, module); - this.server.start() - .then(() => { - debug('http server started', this.server.info.uri); - observer.next(); - observer.complete(); - }) - .catch(err => { - observer.error(err); - observer.complete(); - }); - }); - } - /** - * Lookup into the tree of imports - * and flat the tree into a string array of names + * Initialize HapiJS Server * - * @returns string[] + * @param {CoreModule} module + * @param {HapiConfig} config + * @returns Observable */ - private flattenModules(module: CoreModule): string[] { - const recursive = (_module: CoreModule) => { - if (_module.modules && _module.modules.length > 0) { - return _module.modules.reduce((acc, cur) => acc.concat(recursive(cur)), [].concat(_module.modules)); - } - return []; - }; - debug('flatten modules'); - return recursive(module).map(a => a.name).filter((a, p, arr) => arr.indexOf(a) === p); + onExtensionLoad(module: CoreModule, config: HapiConfig): Observable { + return Observable + .of(new Server(config.options)) + .do(_ => _.connection(Object.assign(config, { options: undefined }))) + .flatMap(server => + Observable + .of({ + instance: this, + token: HttpServerExt, + value: server + }) + ) } /** - * Register each module imported - * as HapiJS plugin + * Build Lifecycle components + * Add routes by modules + * Add Lifecycle handlers + * Start HapiJS Server * - * @param {string[]} names + * @param {CoreModule} module + * @param {Server} server * @returns Observable */ - private registrationObservables(module: CoreModule, server: Server, names: string[]): Observable[] { - return names - .map(n => ModuleManager.findNestedModule(n, module)) - .filter(m => !!m) - .map(m => this.registerPlugin(m, server)); + onModuleInstantiated(module: CoreModule, server: Server): Observable { + return Observable + .from(ModuleManager.getModules(module)) + .flatMap(_ => this.instantiateLifecycle(_, server)) + .flatMap(_ => this.registerPlugin(_, server)) + .reduce((a, c) => a.concat(c), []) + .do(_ => LifecycleManager.routeLifecycle(server, _)) + .flatMap(_ => server.start()); } /** * Register a HapiJS Plugin * * @param {CoreModule} module + * @param {Server} server * @returns Observable */ private registerPlugin(module: CoreModule, server: Server): Observable { - Hoek.assert(!!module, 'Module argument is missing'); - const pluginName = `${module.name}.hapinessplugin`; - debug('registering plugin', pluginName); - return Observable.create((observer) => { - const register: any = (_server, options, next) => { - return next(); - }; - register.attributes = { - name: pluginName, - version: module.version - }; - server.register(register) - .then(() => { - this.addRoutes(module, server) - .subscribe(routes => { - debug('plugin registered', pluginName); - observer.next(routes); - observer.complete(); - }); - }) - .catch((error) => { - observer.error(error); - }); - }); + const register: any = (s, o, n) => n(); + register.attributes = { + name: module.name, + version: module.version + }; + return Observable + .fromPromise(server.register(register)) + .flatMap(_ => this.addRoutes(module, server)); } /** - * Add route from CoreModule + * Add routes from CoreModule * * @param {CoreModule} module * @param {Server} server * @returns Observable */ private addRoutes(module: CoreModule, server: Server): Observable { - Hoek.assert((!!module && !!server), Boom.create(500, 'Please provide module and HapiJS server instance')); - return Observable.create(observer => { - const routes = RouteBuilder.buildRoutes(module) || []; - debug('add routes', module.name, routes.length); - routes.forEach(route => { - const config = Object.assign({ - handler: (req, reply) => { - debug('route handler', req.method, req.path); - HookManager.triggerHook( - enumByMethod(req.method).toString(), - route.token, - req['_hapinessRoute'], - [ req, reply ] - ); - } - }, route.config); - server.route({ - method: route.method, - path: route.path, - config - }); - }); - observer.next(routes); - observer.complete(); - }); + return Observable + .from(RouteBuilder.buildRoutes(module)) + .do(_ => + server + .route({ + method: _.method, + path: _.path, + config: Object.assign({ + handler: (request, reply) => this.httpHandler(request, reply, _) + }, _.config) + }) + ) + .toArray(); } /** + * Trigger the http handler hook + * Reply automatically + * + * @param {Request} request + * @param {ReplyNoContinue} reply + * @param {CoreRoute} route + * @returns void + */ + private httpHandler(request: Request, reply: ReplyNoContinue, route: CoreRoute): void { + HookManager + .triggerHook( + enumByMethod(request.method).toString(), + route.token, + request['_hapinessRoute'], + [ request, reply ] + ) + .map(_ => !!_ && _.statusCode ? _ : { statusCode: 200, response: _ }) + .subscribe( + _ => + reply(_.response) + .code(!!_.response ? _.statusCode : 204), + _ => { + errorHandler(_); + reply(_); + } + ); + } + + /** * Initialize and instantiate lifecycle components * - * @param {Server} server * @param {CoreModule} module + * @param {Server} server */ - private instantiateLifecycle(server: Server, module: CoreModule) { - ModuleManager.getModules(module).forEach(_module => { - [].concat(_module.declarations).filter(decl => !!extractMetadataByDecorator(decl, 'Lifecycle')) - .map(lc => { - debug('add lifecycle', lc.name, _module.token.name); - const metadata = extractMetadataByDecorator(lc, 'Lifecycle'); - server.ext(metadata.event, (request, reply) => { - const instance = DependencyInjection.instantiateComponent(lc, _module.di); - HookManager.triggerHook(LifecycleComponentEnum.OnEvent.toString(), lc, instance, [ request, reply ]); - }); - }); - }); + private instantiateLifecycle(module: CoreModule, server: Server): Observable { + return Observable + .from([].concat(module.declarations)) + .filter(_ => !!_ && !!extractMetadataByDecorator(_, 'Lifecycle')) + .map(_ => ({ metadata: extractMetadataByDecorator(_, 'Lifecycle'), token: _ })) + .do(_ => + server.ext(_.metadata.event, (request: Request, reply: ReplyWithContinue) => { + this + .eventHandler(_.token, module, request, reply) + .subscribe( + () => {}, + err => errorHandler(err) + ) + }) + ) + .toArray() + .map(_ => module); + } + + private eventHandler(lifecycle: Type, module: CoreModule, request: Request, reply: ReplyWithContinue): Observable { + return Observable + .of(lifecycle) + .flatMap(lc => + DependencyInjection + .instantiateComponent(lc, module.di) + .flatMap(_ => + HookManager + .triggerHook(LifecycleComponentEnum.OnEvent.toString(), lc, _, [request, reply]) + ) + ); } } diff --git a/src/extensions/http-server/index.ts b/src/extensions/http-server/index.ts index afc0344..0f97f6f 100644 --- a/src/extensions/http-server/index.ts +++ b/src/extensions/http-server/index.ts @@ -2,4 +2,6 @@ export * from './extension'; export * from './decorators'; export * from './lifecycle'; export * from './route'; +export * from './interfaces'; +export * from './service'; export { Server, Request, ReplyNoContinue, ReplyWithContinue } from 'hapi'; diff --git a/src/extensions/http-server/interfaces.ts b/src/extensions/http-server/interfaces.ts new file mode 100644 index 0000000..60c88ff --- /dev/null +++ b/src/extensions/http-server/interfaces.ts @@ -0,0 +1,186 @@ +import { CoreModule, CoreProvide } from '../../core/interfaces'; +import { Type } from '../../core/decorators'; +import { Request, ReplyWithContinue, ReplyNoContinue, ServerOptions } from 'hapi'; +import { Observable } from 'rxjs'; + +export interface HapiConfig { + host: string; + port: number; + options?: ServerOptions +} + +export interface ValidateConfig { + params?: any; + query?: any; + payload?: any; + response?: any; +} + +export interface RouteConfig { + description?: string; + notes?: string; + tags?: string[]; + validate?: ValidateConfig; + auth?: any; + bind?: any; + cache?: any; + compression?: any; + cors?: any; + ext?: any; + files?: any; + id?: any; + json?: any; + jsonp?: any; + log?: any; + payload?: any; + plugins?: any; + pre?: any; + response?: any; + security?: any; + state?: any; + timeout?: any; +} + +/** + * CoreRoute Type + * Represents an Http Route + */ +export interface CoreRoute { + token: Type | any; + path: string; + method: string | string[]; + module: CoreModule; + providers?: CoreProvide[]; + config?: RouteConfig; +} + +export interface HapinessHTTPHandlerResponse { + response: any; + statusCode: number; +} + +/** + * Route Handler + * called on Http Get request + * + * @returns void | Observable + */ +export interface OnGet { + onGet(request: Request, reply?: ReplyNoContinue): void | Observable; +} + +/** + * Route Handler + * called on Http Post request + * + * @returns void | Observable + */ +export interface OnPost { + onPost(request: Request, reply?: ReplyNoContinue): void | Observable; +} + +/** + * Route Handler + * called on Http Put request + * + * @param {Error} error + * @returns void | Observable + */ +export interface OnPut { + onPut(request: Request, reply?: ReplyNoContinue): void | Observable; +} + +/** + * Route Handler + * called on Http Patch request + * + * @param {string} module + * @returns void | Observable + */ +export interface OnPatch { + onPatch(request: Request, reply?: ReplyNoContinue): void | Observable; +} + +/** + * Route Handler + * called on Http Options request + * + * @param {string} module + * @returns void | Observable + */ +export interface OnOptions { + onOptions(request: Request, reply?: ReplyNoContinue): void | Observable; +} + +/** + * Route Handler + * called on Http Delete request + * + * @param {string} module + * @returns void | Observable + */ +export interface OnDelete { + onDelete(request: Request, reply?: ReplyNoContinue): void | Observable; +} + +/** + * OnPreAuth Lifecycle hook + * + * @param {Request} request + * @param {Reply} reply + * @returns void | Observable + */ +export interface OnPreAuth { + onPreAuth(request: Request, reply?: ReplyWithContinue ): void | Observable; +} + +/** + * OnPostAuth Lifecycle hook + * + * @param {Request} request + * @param {Reply} reply + * @returns void | Observable + */ +export interface OnPostAuth { + onPostAuth(request: Request, reply?: ReplyWithContinue ): void | Observable; +} + +/** + * OnPreHandler Lifecycle hook + * + * @param {Request} request + * @param {Reply} reply + * @returns void | Observable + */ +export interface OnPreHandler { + onPreHandler(request: Request, reply?: ReplyWithContinue ): void | Observable; +} + +/** + * OnPostHandler Lifecycle hook + * + * @param {Request} request + * @param {Reply} reply + * @returns void | Observable + */ +export interface OnPostHandler { + onPostHandler(request: Request, reply?: ReplyWithContinue ): void | Observable; +} + +/** + * OnPreResponse Lifecycle hook + * + * @param {Request} request + * @param {Reply} reply + * @returns void | Observable + */ +export interface OnPreResponse { + onPreResponse(request: Request, reply?: ReplyWithContinue ): void | Observable; +} + +/** + * Request lifecycle component Hook + * + * @returns void | Observable + */ +export interface OnEvent { onEvent(request: Request, reply: ReplyWithContinue): void | Observable; } diff --git a/src/extensions/http-server/lifecycle.ts b/src/extensions/http-server/lifecycle.ts index 1e87e83..b43e249 100644 --- a/src/extensions/http-server/lifecycle.ts +++ b/src/extensions/http-server/lifecycle.ts @@ -1,13 +1,10 @@ +import { Observable } from 'rxjs'; import { HookManager } from '../../core/hook'; import { LifecycleEventsEnum, LifecycleHooksEnum } from './enums'; -import { RouteBuilder, CoreRoute } from './route'; +import { RouteBuilder } from './route'; +import { CoreRoute } from './interfaces'; +import { errorHandler } from '../../core/hapiness'; import { Request, ReplyWithContinue, Server } from 'hapi'; -import * as Debug from 'debug'; -const debug = Debug('lifecycle/hook'); - -export class HttpRequestInfo { - constructor(public id: string) {} -} export class LifecycleManager { @@ -19,38 +16,90 @@ export class LifecycleManager { * * @param {MainModule} main */ - static routeLifecycle(server: Server, routes: CoreRoute[]) { - server.ext(LifecycleEventsEnum.OnPreAuth.toString(), (request: Request, reply: ReplyWithContinue) => { - const route = this.findRoute(request, routes); - /* istanbul ignore else */ - if (route && route.token) { - const reqInfo = new HttpRequestInfo(request.id); - route.providers = route.providers.concat({ provide: HttpRequestInfo, useValue: reqInfo }); - request['_hapinessRoute'] = RouteBuilder.instantiateRouteAndDI(route); - this.eventHandler(LifecycleHooksEnum.OnPreAuth, routes, request, reply); - } else { - reply.continue(); - } - }); + static routeLifecycle(server: Server, routes: CoreRoute[]): void { + + server.ext(LifecycleEventsEnum.OnPreAuth.toString(), + (request: Request, reply: ReplyWithContinue) => + this.instantiateRoute(routes, request, reply) + .subscribe( + _ => reply.continue(), + _ => errorHandler(_) + ) + ); server.ext(LifecycleEventsEnum.OnPostAuth.toString(), - (request, reply) => this.eventHandler(LifecycleHooksEnum.OnPostAuth, routes, request, reply)); + (request: Request, reply: ReplyWithContinue) => + this.eventHandler(LifecycleHooksEnum.OnPostAuth, routes, request, reply) + .subscribe( + _ => reply.continue(), + _ => errorHandler(_) + ) + ); server.ext(LifecycleEventsEnum.OnPreHandler.toString(), - (request, reply) => this.eventHandler(LifecycleHooksEnum.OnPreHandler, routes, request, reply)); + (request: Request, reply: ReplyWithContinue) => + this.eventHandler(LifecycleHooksEnum.OnPostAuth, routes, request, reply) + .subscribe( + _ => reply.continue(), + _ => errorHandler(_) + ) + ); server.ext(LifecycleEventsEnum.OnPostHandler.toString(), - (request, reply) => this.eventHandler(LifecycleHooksEnum.OnPostHandler, routes, request, reply)); + (request: Request, reply: ReplyWithContinue) => + this.eventHandler(LifecycleHooksEnum.OnPostAuth, routes, request, reply) + .subscribe( + _ => reply.continue(), + _ => errorHandler(_) + ) + ); server.ext(LifecycleEventsEnum.OnPreResponse.toString(), - (request, reply) => { - this.eventHandler(LifecycleHooksEnum.OnPreResponse, routes, request, reply); - request['_hapinessRoute'] = undefined; - }); + (request: Request, reply: ReplyWithContinue) => + this.eventHandler(LifecycleHooksEnum.OnPreResponse, routes, request, reply) + .subscribe( + _ => reply.continue(), + _ => errorHandler(_), + () => request['_hapinessRoute'] = undefined + ) + ); + } + + /** + * Instantiate the route matching the request + * And trigger OnPreAuth hook + * + * @param {CoreRoute[]} routes + * @param {Request} request + * @param {ReplyWithContinue} reply + * @returns Observable + */ + private static instantiateRoute(routes: CoreRoute[], request: Request, reply: ReplyWithContinue): Observable { + return Observable + .of(routes) + .map(_ => this.findRoute(request, _)) + .filter(_ => !!(_ && _.token)) + .flatMap(route => + RouteBuilder + .instantiateRouteAndDI(route, request) + .map(_ => ({ route, instance: _ })) + ) + .do(_ => request['_hapinessRoute'] = _.instance) + .defaultIfEmpty(null) + .flatMap(_ => this.eventHandler(LifecycleHooksEnum.OnPreAuth, routes, request, reply)) } + /** + * Find the matching route with + * path and method + * + * @param {Request} request + * @param {CoreRoute[]} routes + * @returns CoreRoute + */ private static findRoute(request: Request, routes: CoreRoute[]): CoreRoute { - return routes.find(r => ((r.method === request.route.method || r.method.indexOf(request.route.method) > -1) && + return routes + .find(r => ((r.method === request.route.method || r.method.indexOf(request.route.method) > -1) && r.path === request.route.path)); } @@ -64,19 +113,20 @@ export class LifecycleManager { * @param {} request * @param {} reply */ - private static eventHandler(hook: LifecycleHooksEnum, routes: CoreRoute[], request, reply) { - const route = this.findRoute(request, routes); - if (request['_hapinessRoute'] && HookManager.hasLifecycleHook(hook.toString(), route.token)) { - HookManager.triggerHook(hook.toString(), route.token, request['_hapinessRoute'], [request, reply]); - } else { - reply.continue(); - } + private static eventHandler(hook: LifecycleHooksEnum, routes: CoreRoute[], + request: Request, reply: ReplyWithContinue): Observable { + + return Observable + .of(routes) + .map(_ => this.findRoute(request, _)) + .filter(_ => request['_hapinessRoute'] && HookManager.hasLifecycleHook(hook.toString(), _.token)) + .flatMap(_ => + HookManager + .triggerHook(hook.toString(), _.token, request['_hapinessRoute'], [request, reply]) + .defaultIfEmpty(null) + ) + .isEmpty() + .filter(_ => !!_); } -} -/** - * Request lifecycle component Hook - * - * @returns void - */ -export interface OnEvent { onEvent(request: Request, reply: ReplyWithContinue): void; } +} diff --git a/src/extensions/http-server/route.ts b/src/extensions/http-server/route.ts index f6bda2e..0107aa1 100644 --- a/src/extensions/http-server/route.ts +++ b/src/extensions/http-server/route.ts @@ -1,63 +1,18 @@ -import { extractMetadataByDecorator } from '../../core/metadata'; -import { Route } from './decorators'; -import { CoreModule, CoreProvide, DependencyInjection } from '../../core'; +import { CoreModule } from '../../core/interfaces'; +import { CoreRoute } from './interfaces'; +import { Observable } from 'rxjs'; import { Type } from '../../core/decorators'; -import { Request, ReplyWithContinue, ReplyNoContinue } from 'hapi'; -import * as Hoek from 'hoek'; -import * as Boom from 'boom'; -import * as Debug from 'debug'; -const debug = Debug('hapiness:extension:httpserver:route'); - -export { Request, ReplyWithContinue, ReplyNoContinue }; +import { Route } from './decorators'; +import { extractMetadataByDecorator } from '../../core/metadata'; +import { DependencyInjection } from '../../core/di'; +import { Request } from 'hapi'; interface InternalType { route: Route; token: Type; } -export interface ValidateConfig { - params?: any; - query?: any; - payload?: any; - response?: any; -} - -export interface RouteConfig { - description?: string; - notes?: string; - tags?: string[]; - validate?: ValidateConfig; - auth?: any; - bind?: any; - cache?: any; - compression?: any; - cors?: any; - ext?: any; - files?: any; - id?: any; - json?: any; - jsonp?: any; - log?: any; - plugins?: any; - pre?: any; - response?: any; - security?: any; - state?: any; - timeout?: any; -} - -/** - * CoreRoute Type - * Represents an Http Route - */ -export interface CoreRoute { - token: Type | any; - path: string; - method: string | string[]; - module: CoreModule; - providers?: CoreProvide[]; - config?: RouteConfig; -} +export class HttpRequestInfo {} export class RouteBuilder { @@ -75,37 +30,36 @@ export class RouteBuilder { * @param {CoreModule} module * @returns CoreRoute */ - public static buildRoutes(module: CoreModule): CoreRoute[] { - Hoek.assert(!!module, Boom.create(500, 'Please provide a module')); - debug('build routes', module.name); - const routes = this.metadataFromDeclarations(module.declarations) - .map(data => this.coreRouteFromMetadata(data.route, data.token, module)); - return routes; + public static buildRoutes(module: CoreModule): Observable { + return Observable + .of(module) + .filter(_ => !!_) + .flatMap(_ => this.metadataFromDeclarations(_.declarations)) + .flatMap(_ => this.coreRouteFromMetadata(_.route, _.token, module)); } /** * Instantiate a new Route * with its own DI/request * - * /!\ Create a DI/req give some - * perf issues. Still investigating - * for a solution. For now, we provide - * just the DI's module - * * @param {CoreRoute} route - * @returns Type + * @param {Request} request + * @returns Observable */ - public static instantiateRouteAndDI(route: CoreRoute): Type { - debug('instantiate route', route.method, route.path); - if (route.providers && route.providers.length > 0) { - console.warn(`${route.path} - Providers for a route are not available`); - } - return DependencyInjection.instantiateComponent( - >route.token, - /*DependencyInjection.createAndResolve(route.providers - .map(p => Object.assign(>{}, p)), route.module.di - )*/route.module.di - ); + public static instantiateRouteAndDI(route: CoreRoute, request: Request): Observable { + return Observable + .of(request) + .map(_ => ({ + query: Object.assign({}, request.query), + params: Object.assign({}, request.params), + headers: Object.assign({}, request.headers), + payload: Object.assign({}, request.payload), + id: request.id + })) + .map(_ => ({ provide: HttpRequestInfo, useValue: _ })) + .map(_ => [].concat(route.providers).concat(_)) + .flatMap(_ => DependencyInjection.createAndResolve(_, route.module.di)) + .flatMap(_ => DependencyInjection.instantiateComponent(route.token, _)); } /** @@ -114,19 +68,27 @@ export class RouteBuilder { * @param {Route} data * @returns CoreRoute */ - private static coreRouteFromMetadata(data: Route, token: Type, module: CoreModule): CoreRoute { - const providers = data.providers || []; - const method = Array.isArray(data.method) ? - data.method.map(_ => _.toLowerCase()) : - data.method.toString().toLowerCase(); - return { - token, - module, - config: data.config, - path: data.path, - method, - providers: providers.map((p: any) => !!p.provide ? p : { provide: p, useClass: p }) - }; + private static coreRouteFromMetadata(data: Route, token: Type, module: CoreModule): Observable { + return Observable + .of(data) + .flatMap(_ => + Observable + .from([].concat(_.method)) + .map(__ => __.toLowerCase()) + .toArray() + .map(__ => ({ data: _, methods: __ })) + ) + .map(_ => ({ + token, + module, + config: _.data.config, + path: _.data.path, + method: _.methods, + providers: [] + .concat(_.data.providers) + .filter(p => !!p) + .map(p => p.provide ? p : { provide: p, useClass: p }) + })); } /** @@ -136,113 +98,10 @@ export class RouteBuilder { * @param {Type} declarations * @returns Route */ - private static metadataFromDeclarations(declarations: Type | Type[]): InternalType[] { - debug('metadata from declarations', declarations.length); - return [].concat(declarations) - .map(t => { - return { - route: extractMetadataByDecorator(t, this.decoratorName), - token: >t - }; - }) - .filter(t => !!t.route); + private static metadataFromDeclarations(declarations: Type[]): Observable { + return Observable + .from([].concat(declarations)) + .filter(_ => !!_ && !!extractMetadataByDecorator(_, this.decoratorName)) + .map(_ => ({ token: _, route: extractMetadataByDecorator(_, this.decoratorName) })) } - } - -/** - * Route Handler - * called on Http Get request - * - * @returns void - */ -export interface OnGet { onGet(request: Request, reply: ReplyNoContinue): void; } - -/** - * Route Handler - * called on Http Post request - * - * @returns void - */ -export interface OnPost { onPost(request: Request, reply: ReplyNoContinue): void; } - -/** - * Route Handler - * called on Http Put request - * - * @param {Error} error - * @returns void - */ -export interface OnPut { onPut(request: Request, reply: ReplyNoContinue): void; } - -/** - * Route Handler - * called on Http Patch request - * - * @param {string} module - * @returns void - */ -export interface OnPatch { onPatch(request: Request, reply: ReplyNoContinue): void; } - -/** - * Route Handler - * called on Http Options request - * - * @param {string} module - * @returns void - */ -export interface OnOptions { onOptions(request: Request, reply: ReplyNoContinue): void; } - -/** - * Route Handler - * called on Http Delete request - * - * @param {string} module - * @returns void - */ -export interface OnDelete { onDelete(request: Request, reply: ReplyNoContinue): void; } - -/** - * OnPreAuth Lifecycle hook - * - * @param {Request} request - * @param {Reply} reply - * @returns void - */ -export interface OnPreAuth { onPreAuth(request: Request, reply: ReplyWithContinue ): void; } - -/** - * OnPostAuth Lifecycle hook - * - * @param {Request} request - * @param {Reply} reply - * @returns void - */ -export interface OnPostAuth { onPostAuth(request: Request, reply: ReplyWithContinue ): void; } - -/** - * OnPreHandler Lifecycle hook - * - * @param {Request} request - * @param {Reply} reply - * @returns void - */ -export interface OnPreHandler { onPreHandler(request: Request, reply: ReplyWithContinue ): void; } - -/** - * OnPostHandler Lifecycle hook - * - * @param {Request} request - * @param {Reply} reply - * @returns void - */ -export interface OnPostHandler { onPostHandler(request: Request, reply: ReplyWithContinue ): void; } - -/** - * OnPreResponse Lifecycle hook - * - * @param {Request} request - * @param {Reply} reply - * @returns void - */ -export interface OnPreResponse { onPreResponse(request: Request, reply: ReplyWithContinue ): void; } diff --git a/src/extensions/http-server/service.ts b/src/extensions/http-server/service.ts new file mode 100644 index 0000000..569a724 --- /dev/null +++ b/src/extensions/http-server/service.ts @@ -0,0 +1,31 @@ +import { Injectable, Inject } from '../../core'; +import { HttpServerExt, Server } from './'; +import { Observable } from 'rxjs'; + +@Injectable() +export class HttpServerService { + + constructor( + @Inject(HttpServerExt) private httpServer: Server + ) {} + + /** + * Get HapiJS Server instance + * + * @returns Server + */ + instance(): Server { + return this.httpServer; + } + + /** + * Stop the HapiJS Server + * + * @returns Observable + */ + stop(): Observable { + return Observable + .fromPromise(this.httpServer.stop()); + } + +} diff --git a/src/extensions/socket-server/extension.ts b/src/extensions/socket-server/extension.ts index 30af0ec..f517550 100644 --- a/src/extensions/socket-server/extension.ts +++ b/src/extensions/socket-server/extension.ts @@ -1,9 +1,6 @@ -import { CoreModule, Extension, ExtensionWithConfig, OnExtensionLoad } from '../../core'; +import { CoreModule, Extension, ExtensionWithConfig, OnExtensionLoad } from '../../core/interfaces'; import { Observable } from 'rxjs/Observable'; -import { server } from 'websocket'; import { WebSocketServer } from './server'; -import * as Debug from 'debug'; -const debug = Debug('hapiness:extension:socketserver'); export interface SocketConfig { port: number; @@ -11,12 +8,14 @@ export interface SocketConfig { keepaliveInterval?: number; keepaliveGracePeriod?: number; closeTimeout?: number; + tls?: { + key: Buffer; + cert: Buffer; + } } export class SocketServerExt implements OnExtensionLoad { - private server: server; - public static setConfig(config: SocketConfig): ExtensionWithConfig { return { token: SocketServerExt, @@ -33,15 +32,12 @@ export class SocketServerExt implements OnExtensionLoad { * @returns Observable */ onExtensionLoad(module: CoreModule, config: SocketConfig): Observable { - debug('server instantiation'); - const instance = new WebSocketServer(config); - return Observable.create(observer => { - observer.next({ + return Observable + .of(new WebSocketServer(config)) + .map(_ => ({ instance: this, token: SocketServerExt, - value: instance - }); - observer.complete(); - }) + value: _ + })); } } diff --git a/src/extensions/socket-server/index.ts b/src/extensions/socket-server/index.ts index cce0a84..20ac8c8 100644 --- a/src/extensions/socket-server/index.ts +++ b/src/extensions/socket-server/index.ts @@ -1,3 +1,4 @@ export * from './extension'; export * from './socket'; export * from './server'; +export * from './service'; diff --git a/src/extensions/socket-server/server.ts b/src/extensions/socket-server/server.ts index 165d2d0..a3cbc43 100644 --- a/src/extensions/socket-server/server.ts +++ b/src/extensions/socket-server/server.ts @@ -2,6 +2,7 @@ import { server, request } from 'websocket'; import { Socket } from './socket'; import { SocketConfig } from './extension'; import * as http from 'http'; +import * as https from 'https'; import * as Debug from 'debug'; const debug = Debug('hapiness:extension:socketserver'); @@ -10,17 +11,22 @@ export class WebSocketServer { private server: server; private subscribers: Array<(socket: Socket) => void>; private sockets: Socket[]; + private httpServer: http.Server | https.Server; constructor(config: SocketConfig) { - const httpServer = http.createServer((_request, _response) => { - /* istanbul ignore next */ + /* istanbul ignore next */ + const httpHandler = (_request, _response) => { _response.writeHead(404); - /* istanbul ignore next */ _response.end(); - }); - httpServer.listen(config.port); + }; + if (!!config.tls) { + this.httpServer = https.createServer(config.tls, httpHandler); + } else { + this.httpServer = http.createServer(httpHandler); + } + this.httpServer.listen(config.port); delete config.port; - const _config = Object.assign({ httpServer }, config); + const _config = Object.assign({ httpServer: this.httpServer }, config); this.server = new server(_config); this.sockets = []; this.subscribers = []; @@ -79,4 +85,8 @@ export class WebSocketServer { public getServer() { return this.server; } + + public getHttpServer() { + return this.httpServer; + } } diff --git a/src/extensions/socket-server/service.ts b/src/extensions/socket-server/service.ts new file mode 100644 index 0000000..e9627e9 --- /dev/null +++ b/src/extensions/socket-server/service.ts @@ -0,0 +1,35 @@ +import { Injectable, Inject } from '../../core'; +import { WebSocketServer } from './server'; +import { SocketServerExt } from './extension'; +import { Observable } from 'rxjs'; + +@Injectable() +export class SocketServerService { + + constructor( + @Inject(SocketServerExt) private socketServer: WebSocketServer + ) {} + + /** + * Get WebSocket Server instance + * + * @returns Server + */ + instance(): WebSocketServer { + return this.socketServer; + } + + /** + * Stop the WebSocket Server + * + * @returns Observable + */ + stop(): Observable { + return Observable + .of(this.socketServer) + .do(_ => _.getHttpServer().close()) + .do(_ => _.getServer().shutDown()) + .map(_ => null); + } + +} diff --git a/src/index.ts b/src/index.ts index d9928f7..90bd025 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ export { Hapiness, HapinessModule, Inject, Injectable, Optional, Lib, InjectionToken, - CoreModuleWithProviders, OnRegister, OnStart, CoreModule, CoreDecorator, CoreProvide, + CoreModuleWithProviders, OnRegister, OnStart, OnError, CoreModule, CoreDecorator, CoreProvide, Extension, ExtensionWithConfig, makeDecorator, Type, OnModuleInstantiated, OnExtensionLoad } from './core'; export { HttpServerExt, HapiConfig, Route, Lifecycle, HttpRequestInfo, OnEvent, OnGet, OnDelete, OnOptions, OnPatch, OnPost, OnPut, OnPreAuth, OnPostAuth, OnPreHandler, OnPostHandler, OnPreResponse, Request, - ReplyWithContinue, ReplyNoContinue, Server } from './extensions/http-server'; + ReplyWithContinue, ReplyNoContinue, Server, HttpServerService } from './extensions/http-server'; -export { SocketServerExt, Socket, SocketConfig, WebSocketServer } from './extensions/socket-server'; +export { SocketServerExt, Socket, SocketConfig, WebSocketServer, SocketServerService } from './extensions/socket-server'; diff --git a/test/integration/core.test.ts b/test/integration/core.test.ts index 8957942..f48e2f6 100644 --- a/test/integration/core.test.ts +++ b/test/integration/core.test.ts @@ -1,226 +1,177 @@ import { suite, test } from 'mocha-typescript'; -import { Observable } from 'rxjs/Observable'; +import { Hapiness, HapinessModule, OnStart, OnRegister, Lib, Injectable } from '../../src/core'; +import { Observable } from 'rxjs'; import * as unit from 'unit.js'; -import { - Hapiness, - HapinessModule, - Injectable, - OnStart, - OnRegister, - Lib, - InjectionToken, - Inject -} from '../../src/core'; @suite('Integration - Core') -class CoreIntegration { +class ModuleTestSuite { - @test('HapinessModule') - test1(done) { + @test('Bootstrap - Simple module') + testBootstrap1(done) { @HapinessModule({ - version: '1.0.0' + version: '' }) - class ModuleTest implements OnStart { + class Module1 implements OnStart { + onStart() { - unit.string(Hapiness['module'].name) - .is('ModuleTest'); done(); } + } - Hapiness.bootstrap(ModuleTest); + Hapiness + .bootstrap(Module1); + } - @test('HapinessModule - DI') - test2(done) { + @test('Bootstrap - Module with embedded module') + testBootstrap2(done) { + let state = 0; - @Injectable() - class Service1 { - getData() { - return 'test'; + @HapinessModule({ + version: '' + }) + class Module1 implements OnRegister { + + onRegister() { + state = 1; } + } @HapinessModule({ - version: '1.0.0', - providers: [ Service1 ] + version: '', + imports: [ Module1 ] }) - class ModuleTest implements OnStart { - - constructor(private service: Service1) {} + class Module2 implements OnStart { onStart() { - unit.string(this.service.getData()) - .is('test'); + unit + .value(state) + .is(1); done(); } + } - Hapiness.bootstrap(ModuleTest); + Hapiness + .bootstrap(Module2); + } - @test('HapinessModule - SubModule') - test3(done) { + @test('Bootstrap - Module with Lib') + testBootstrap3(done) { - @Injectable() - class Service1 { - getData() { - return 'test'; - } - } - - @Injectable() - class Service2 { - getData() { - return '123'; + @Lib() + class Lib1 { + constructor() { + done(); } } @HapinessModule({ - version: '1.0.0', - providers: [ Service2 ] + version: '', + declarations: [ Lib1 ] }) - class SubSubModule implements OnRegister { + class Module1 {} - constructor(private service: Service2) {} - - onRegister() { - unit.string(this.service.getData()).is('123'); - } - } + Hapiness + .bootstrap(Module1); - @HapinessModule({ - version: '1.0.0', - providers: [ Service2 ], - exports: [ Service2 ], - imports: [{ module: SubSubModule, providers: [] }] - }) - class SubModule implements OnRegister { + } - constructor(private service: Service2) {} + @test('Bootstrap - Module with Provider') + testBootstrap4(done) { - onRegister() { - unit.string(this.service.getData()).is('123'); + @Injectable() + class Provider1 { + value() { + return 123; } } @HapinessModule({ - version: '1.0.0', - providers: [ Service1 ], - imports: [ SubModule ] + version: '', + providers: [ Provider1 ] }) - class ModuleTest implements OnStart { - - constructor( - private service1: Service1, - private service2: Service2 - ) {} - + class Module1 implements OnStart { + constructor(private provider1: Provider1) {} onStart() { - unit.string(this.service1.getData() + this.service2.getData()) - .is('test123'); + unit + .number(this.provider1.value()) + .is(123); done(); } } - Hapiness.bootstrap(ModuleTest); + Hapiness + .bootstrap(Module1); + } - @test('HapinessModule - Libs') - test4(done) { + @test('Bootstrap - Module with Lib and Provider') + testBootstrap5(done) { @Injectable() - class Service1 { - getData() { - return 'test'; + class Provider1 { + value() { + return 123; } } @Lib() - class LibTest { - constructor(private service: Service1) { - unit.string(this.service.getData()) - .is('test'); - done(); + class Lib1 { + constructor(provider1: Provider1) { + unit + .number(provider1.value()) + .is(123); } } @HapinessModule({ - version: '1.0.0', - providers: [ Service1 ], - declarations: [ LibTest ] + version: '', + declarations: [ Lib1 ], + providers: [ Provider1 ] }) - class ModuleTest {} - - Hapiness.bootstrap(ModuleTest); - } - - @test('HapinessModule - Error') - test5(done) { - - @HapinessModule({ - version: '1.0.0' - }) - class ModuleTest { - + class Module1 implements OnStart { + constructor(private provider1: Provider1) {} onStart() { - return Observable.create(observer => { - observer.error(new Error('error')); - observer.complete(); - }); + unit + .number(this.provider1.value()) + .is(123); + done(); } } - Hapiness.bootstrap(ModuleTest).catch(_ => { - unit.object(_) - .isInstanceOf(Error) - .hasProperty('message', 'error'); - done(); - }); - } + Hapiness + .bootstrap(Module1); - @test('HapinessModule - Provide parent config to sub module') - test6(done) { + } - const TOKEN = new InjectionToken('token'); + @test('Bootstrap - Error thrown') + testBootstrap6(done) { @HapinessModule({ - version: '1.0.0' + version: '' }) - class SubModuleTest { - - constructor(@Inject(TOKEN) private config) {} + class Module1 implements OnStart { - onRegister() { - unit.must(this.config.test) - .is(true); - done(); + onStart() { + throw new Error('Oops'); } - } - @HapinessModule({ - version: '1.0.0', - imports: [ SubModuleTest ] - }) - class ModuleTest { - - static setConfig(config) { - return { - module: ModuleTest, - providers: [ - { provide: TOKEN, useValue: config } - ] - } - } } - @HapinessModule({ - version: '1.0.0', - imports: [ ModuleTest.setConfig({ test: true }) ] - }) - class AppModuleTest {} + Hapiness + .bootstrap(Module1) + .catch(_ => { + unit + .object(_) + .isInstanceOf(Error) + .hasProperty('message', 'Oops'); + done(); + }); - Hapiness.bootstrap(AppModuleTest); } } diff --git a/test/integration/http-server.test.ts b/test/integration/http-server.test.ts index 621f6ab..7d4d07c 100644 --- a/test/integration/http-server.test.ts +++ b/test/integration/http-server.test.ts @@ -1,8 +1,9 @@ -import { suite, test} from 'mocha-typescript'; +import { suite, test, only } from 'mocha-typescript'; import * as unit from 'unit.js'; -import { Hapiness, HapinessModule, Inject, OnRegister, OnStart } from '../../src/core'; -import { HttpServerExt, Route, Lifecycle, OnGet, OnEvent, OnPreResponse } from '../../src/extensions/http-server'; +import { Hapiness, HapinessModule, Inject, OnRegister, OnStart, OnError } from '../../src/core'; +import { HttpServerExt, Route, Lifecycle, OnGet, OnEvent, OnPreResponse, HttpServerService } from '../../src/extensions/http-server'; import { Server } from 'hapi'; +import { Observable } from 'rxjs'; @suite('Integration - Http Server') class HttpServerIntegration { @@ -22,16 +23,17 @@ class HttpServerIntegration { @HapinessModule({ version: '1.0.0', - declarations: [ RouteTest ] + declarations: [ RouteTest ], + providers: [ HttpServerService ] }) class ModuleTest implements OnStart { - constructor(@Inject(HttpServerExt) private server: Server) {} + constructor(private server: HttpServerService) {} onStart() { - this.server.inject('/', res => { + this.server.instance().inject('/', res => { unit.must(res.result).equal('test'); - this.server.stop().then(_ => done()); + this.server.stop().subscribe(_ => done()); }); } } @@ -101,69 +103,6 @@ class HttpServerIntegration { .catch(_ => done(_)); } - /* @test('lifecycle') - test2(done) { - - class Service1 { - getData() { - return '123'; - } - } - class Service2 {} - class Service3 { - getData() { - return '456'; - } - } - - @Route({ - path: '/', - method: 'GET', - providers: [ Service1, { provide: Service2, useClass: Service2 } ] - }) - class RouteTest implements OnGet, OnPreResponse { - constructor(private serv: Service1, private serv3: Service3) {} - onGet(request, reply) { - reply('x'); - } - onPreResponse(request, reply) { - request.response.source = request.response.source + this.serv.getData() + this.serv3.getData(); - reply.continue(); - } - } - - @Lifecycle({ - event: 'onPostHandler' - }) - class LF implements OnEvent { - onEvent(request, reply) { - request.response.source = 'toto'; - reply.continue(); - } - } - - @HapinessModule({ - version: '1.0.0', - declarations: [ RouteTest, LF ], - providers: [ Service3 ] - }) - class ModuleTest implements OnStart { - - constructor(@Inject(HttpServerExt) private server: Server) {} - - onStart() { - this.server.inject('/', res => { - unit.string(res.result) - .is('toto123456'); - this.server.stop().then(_ => done()); - }); - } - } - - Hapiness.bootstrap(ModuleTest, [ HttpServerExt.setConfig({ host: '0.0.0.0', port: 4444 }) ]) - .catch(_ => done(_)); - }*/ - @test('route submodule') test3(done) { @@ -199,8 +138,7 @@ class HttpServerIntegration { } } - Hapiness.bootstrap(ModuleTest, [ HttpServerExt.setConfig({ host: '0.0.0.0', port: 4444 }) ]) - .catch(_ => console.log(_.message)); + Hapiness.bootstrap(ModuleTest, [ HttpServerExt.setConfig({ host: '0.0.0.0', port: 4444 }) ]); } @test('port already used') @@ -251,4 +189,108 @@ class HttpServerIntegration { Hapiness.bootstrap(ModuleTest, [ HttpServerExt.setConfig({ host: '0.0.0.0', port: 4445 }) ]); } + + @test('response with Observable') + test6(done) { + + @Route({ + path: '/', + method: 'GET', + }) + class RouteTest implements OnGet { + onGet(request, reply) { + return Observable + .of('test'); + } + } + + @Route({ + path: '/2', + method: 'GET', + }) + class RouteTest2 implements OnGet { + onGet(request, reply) { + reply('test2'); + } + } + + @Route({ + path: '/3', + method: 'GET', + }) + class RouteTest3 implements OnGet { + onGet(request, reply) { + return Observable + .of({ response: 'test3', statusCode: 201 }); + } + } + + @HapinessModule({ + version: '1.0.0', + declarations: [ RouteTest, RouteTest2, RouteTest3 ] + }) + class ModuleTest implements OnStart { + constructor(@Inject(HttpServerExt) private server: Server) {} + onStart() { + this.server.inject('/', res => { + unit.string(res.result) + .is('test'); + unit.value(res.statusCode) + .is(200) + this.server.inject('/2', res2 => { + unit.string(res2.result) + .is('test2'); + this.server.inject('/3', res3 => { + unit.string(res3.result) + .is('test3'); + unit.value(res3.statusCode) + .is(201) + this.server.stop().then(_ => done()); + }); + }); + }); + } + } + + Hapiness.bootstrap(ModuleTest, [ HttpServerExt.setConfig({ host: '0.0.0.0', port: 4445 }) ]); + } + + @test('throw error in route') + test7(done) { + + @Route({ + path: '/', + method: 'GET', + }) + class RouteTest implements OnGet { + onGet(request, reply) { + throw new Error('Oops'); + } + } + + @HapinessModule({ + version: '1.0.0', + declarations: [ RouteTest ] + }) + class ModuleTest implements OnStart, OnError { + + constructor(@Inject(HttpServerExt) private server: Server) {} + + onStart() { + this.server.inject('/', res => { + unit.must(res.statusCode).equal(500); + }); + } + + onError(error) { + unit + .object(error) + .isInstanceOf(Error) + .hasProperty('message', 'Oops'); + this.server.stop().then(_ => done()); + } + } + + Hapiness.bootstrap(ModuleTest, [ HttpServerExt.setConfig({ host: '0.0.0.0', port: 4445 }) ]); + } } diff --git a/test/integration/socket-server.test.ts b/test/integration/socket-server.test.ts index 28ed769..95ff5da 100644 --- a/test/integration/socket-server.test.ts +++ b/test/integration/socket-server.test.ts @@ -1,7 +1,7 @@ import { suite, test } from 'mocha-typescript'; import * as unit from 'unit.js'; import { Hapiness, HapinessModule, Inject, OnStart } from '../../src/core'; -import { SocketServerExt, WebSocketServer } from '../../src/extensions/socket-server'; +import { SocketServerExt, WebSocketServer, SocketServerService } from '../../src/extensions/socket-server'; @suite('Integration - Socket Server') class SocketServerIntegration { @@ -10,22 +10,23 @@ class SocketServerIntegration { test1(done) { @HapinessModule({ - version: '1.0.0' + version: '1.0.0', + providers: [ SocketServerService ] }) class ModuleTest implements OnStart { - constructor(@Inject(SocketServerExt) private server: WebSocketServer) {} + constructor(private server: SocketServerService) {} onStart() { - this.server.onRequest(socket => { - unit.array(this.server.getSockets()) + this.server.instance().onRequest(socket => { + unit.array(this.server.instance().getSockets()) .hasLength(1); socket.emit('toto', 'test'); socket.on('close', data => {}); socket.on('error', data => {}); socket.on('tata', data => {}); socket.on('ev', data => { - this.server.broadcast('test', 'test'); + this.server.instance().broadcast('test', 'test'); }); socket.on('*', data => { if (data.utf8Data === '123') { @@ -38,8 +39,8 @@ class SocketServerIntegration { unit.string(data.toString()) .is('test'); socket.close(); - this.server.getServer().closeAllConnections(); - done(); + this.server.stop() + .subscribe(_ => done()); }); }); @@ -59,4 +60,20 @@ class SocketServerIntegration { Hapiness.bootstrap(ModuleTest, [ SocketServerExt.setConfig({ port: 2222 }) ]); } + + @test('did well stop') + test2(done) { + + @HapinessModule({ + version: '1.0.0', + providers: [ SocketServerService ] + }) + class ModuleTest implements OnStart { + + onStart() { + done(); + } + } + Hapiness.bootstrap(ModuleTest, [ SocketServerExt.setConfig({ port: 2222 }) ]); + } } diff --git a/test/unit/bootstrap.unit.ts b/test/unit/bootstrap.unit.ts deleted file mode 100644 index 4e63903..0000000 --- a/test/unit/bootstrap.unit.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { Hapiness } from '../../src/core/bootstrap'; -import { HookManager } from '../../src/core/hook'; -import { ModuleManager } from '../../src/core/module'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/observable/throw'; -import { only, suite, test } from 'mocha-typescript'; -import * as unit from 'unit.js'; - -@only -@suite('Unit - Bootstrap') -class SuiteBoostrap { - - @test('bootstrap - no module') - test1(done) { - - Hapiness.bootstrap(null) - .catch(_ => { - unit.object(_) - .isInstanceOf(Error) - .hasProperty('message', 'Please provide a module to bootstrap'); - done(); - }); - } - - @test('bootstrap - wrong module') - test2(done) { - - Hapiness.bootstrap('test') - .catch(_ => { - unit.object(_) - .isInstanceOf(Error) - .hasProperty('message', 'Wrong module to bootstrap'); - done(); - }); - } - - @test('bootstrap - no extensions') - test3(done) { - - class TestModule {} - const cm = { - token: TestModule, - name: 'TestModule', - version: '' - } - const cmi = Object.assign({ instance: new TestModule() }, cm); - - const mocks = []; - mocks.push(unit.stub(ModuleManager, 'resolveModule') - .returns(cm)); - mocks.push(unit.stub(ModuleManager, 'instantiateModule') - .returns(Observable.create(observer => { - observer.next(cmi); - observer.complete(); - }))); - mocks.push(unit.stub(ModuleManager, 'getModules') - .returns(Observable.create(observer => { - observer.next([ cmi ]); - observer.complete(); - }))); - mocks.push(unit.stub(HookManager, 'hasLifecycleHook') - .returns(false)); - mocks.push(unit.stub(HookManager, 'triggerHook') - .returns(Observable.create(observer => { - observer.next(); - observer.complete(); - }))); - - Hapiness.bootstrap(TestModule) - .then(_ => { - unit.must(Hapiness['module']).equal(cmi); - unit.array(Hapiness['extensions']).is([]); - mocks.forEach(__ => __.restore()); - done(); - }); - } - - @test('bootstrap - with extension') - test4(done) { - - class TestModule {} - class TestExt {} - const cm = { - token: TestModule, - name: 'TestModule', - version: '' - } - const cmi = Object.assign({ instance: new TestModule() }, cm); - const cmic = Object.assign({ parent: cmi }, cmi); - - const mocks = []; - mocks.push(unit.stub(ModuleManager, 'resolveModule') - .returns(cm)); - mocks.push(unit.stub(ModuleManager, 'instantiateModule') - .returns(Observable.create(observer => { - observer.next(cmi); - observer.complete(); - }))); - mocks.push(unit.stub(ModuleManager, 'getModules') - .returns(Observable.create(observer => { - observer.next([ cmi, cmic ]); - observer.complete(); - }))); - mocks.push(unit.stub(HookManager, 'hasLifecycleHook') - .returns(true)); - mocks.push(unit.stub(HookManager, 'triggerHook') - .returns(Observable.create(observer => { - observer.next(); - observer.complete(); - }))); - - mocks.push(unit.stub(Hapiness, 'loadExtention') - .returns(Observable.create(observer => { - observer.next({ - token: TestExt, - instance: new TestExt(), - value: {} - }); - observer.complete(); - }))); - - Hapiness.bootstrap(TestModule, [ TestExt ]) - .then(_ => { - unit.must(Hapiness['module']).equal(cmi); - unit.array(Hapiness['extensions']).isNotEmpty(); - mocks.forEach(__ => __.restore()); - done(); - }); - } - - @test('toExtensionWithConfig') - test5() { - - class Token {} - unit.object(Hapiness['toExtensionWithConfig'](Token)) - .is({ - token: Token, - config: {} - }); - - unit.object(Hapiness['toExtensionWithConfig']({ - token: Token, - config: { - test: true - } - })) - .is({ - token: Token, - config: { - test: true - } - }); - } - - @test('loadExtention') - test6(done) { - class Token { - onExtensionLoad() { - return Observable.create(observer => { - observer.next(true); - observer.complete(); - }); - } - } - Hapiness['loadExtention']({ - token: Token, - config: {} - }).subscribe(_ => { - unit.must(_).equal(true); - done(); - }); - } -} diff --git a/test/unit/di.test.ts b/test/unit/di.test.ts new file mode 100644 index 0000000..0398a6d --- /dev/null +++ b/test/unit/di.test.ts @@ -0,0 +1,140 @@ +import { Injectable } from '../../src/core'; +import { suite, test } from 'mocha-typescript'; +import { DependencyInjection, Lib } from '../../src/core'; +import { Observable } from 'rxjs'; +import * as unit from 'unit.js'; + +import { EmptyProvider } from './mocks'; + +@suite('Unit - DI') +class ModuleTestSuite { + + @test('createAndResolve - provide providers without parent and must return DI') + testCreateAndResolve1() { + + DependencyInjection + .createAndResolve([ EmptyProvider ]) + .subscribe( + _ => + unit + .object(_.get(EmptyProvider)) + .isInstanceOf(EmptyProvider) + ); + + } + + @test('createAndResolve - provide providers with parent and must return DI') + testCreateAndResolve2() { + + @Injectable() + class ParentProvider {} + + DependencyInjection + .createAndResolve([ ParentProvider ]) + .flatMap(_ => DependencyInjection.createAndResolve([ EmptyProvider ], _)) + .subscribe( + _ => + unit + .object(_.get(ParentProvider)) + .isInstanceOf(ParentProvider) + ); + + } + + @test('createAndResolve - provide provider that thrown error') + testCreateAndResolve3() { + + @Injectable() + class ParentProvider { + constructor() { throw new Error('Oops'); } + } + + DependencyInjection + .createAndResolve([ EmptyProvider ]) + .subscribe( + null, + _ => + unit + .object(_) + .isInstanceOf(Error) + .hasProperty('message', 'Oops') + ); + + } + + @test('createAndResolve - provide parent and provide an already injected provider') + testCreateAndResolve4() { + + let count = 0; + + @Injectable() + class TestProvider { + constructor() { count++; } + } + + DependencyInjection + .createAndResolve([ TestProvider ]) + .do(_ => _.get(TestProvider)) + .flatMap(_ => DependencyInjection.createAndResolve([ TestProvider ], _)) + .subscribe( + _ => { + _.get(TestProvider); + unit + .number(count) + .is(2); + } + ); + + } + + @test('instantiateComponent - instantiate Lib component with provider dependency') + testInstantiateComponent1() { + + let count = 0; + + @Lib() + class TestLib { + constructor(public provider: EmptyProvider) {} + } + + DependencyInjection + .createAndResolve([ EmptyProvider ]) + .flatMap(_ => + DependencyInjection + .instantiateComponent(TestLib, _) + ) + .subscribe( + _ => + unit + .object(_.provider) + .isInstanceOf(EmptyProvider) + ); + + } + + @test('instantiateComponent - instantiate Lib and with no provider') + testInstantiateComponent2() { + + let count = 0; + + @Lib() + class TestLib { + constructor(public provider: EmptyProvider) {} + } + + DependencyInjection + .createAndResolve([]) + .flatMap(_ => + DependencyInjection + .instantiateComponent(TestLib, _) + ) + .subscribe( + null, + _ => + unit + .object(_) + .isInstanceOf(Error) + ); + + } +} diff --git a/test/unit/di.unit.ts b/test/unit/di.unit.ts deleted file mode 100644 index 31bd560..0000000 --- a/test/unit/di.unit.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { test, suite } from 'mocha-typescript'; -import * as unit from 'unit.js'; -import { Injectable, Inject, Lib, DependencyInjection } from '../../src/core'; - -class MyService { - method() { - return 'hello'; - } -} - -@Injectable() -class MyService2 { - constructor(private svc: MyService) {} - my() { - return this.svc.method(); - } -} - -@suite('Unit - Dependency Injection') -class DI { - - @test('service injection') - testInjection() { - - const injector = DependencyInjection.createAndResolve([MyService, MyService2]); - unit.must(injector.get(MyService2).my()).equal('hello'); - - } - - @test('service injection - extends') - testInjectionExtends() { - - const injector = DependencyInjection.createAndResolve([MyService, MyService2]); - - @Injectable() - class MyOwnService { - constructor(private svc: MyService2) {} - myTest() { - return this.svc.my(); - } - } - - const myInjector = DependencyInjection.createAndResolve([MyOwnService], injector); - - unit.must(myInjector.get(MyOwnService).myTest()).equal('hello'); - - } - - @test('service injection - error') - testInjectionError() { - - const injector = DependencyInjection.createAndResolve([MyService2]); - try { - const svc = injector.get(MyService2); - } catch (e) { - unit.must(e.message).contain('No provider for MyService!'); - } - - } - - @test('instantiate a component') - testInstantiate() { - @Injectable() - class Service { - meth() { return 1; } - } - @Lib() - class Component { - constructor(@Inject(Service) public serv: Service) {} - } - const injector = DependencyInjection.createAndResolve([ Service ]); - unit.must(DependencyInjection.instantiateComponent(Component, injector).serv.meth()).equal(1); - } -} diff --git a/test/unit/http-server/enums.unit.ts b/test/unit/extensions/http-server/enums.test.ts similarity index 90% rename from test/unit/http-server/enums.unit.ts rename to test/unit/extensions/http-server/enums.test.ts index dc8f1f9..fd4d116 100644 --- a/test/unit/http-server/enums.unit.ts +++ b/test/unit/extensions/http-server/enums.test.ts @@ -1,6 +1,6 @@ import { suite, test } from 'mocha-typescript'; import * as unit from 'unit.js'; -import { RouteMethodsEnum, enumByMethod } from '../../../src/extensions/http-server/enums'; +import { RouteMethodsEnum, enumByMethod } from '../../../../src/extensions/http-server/enums'; @suite('Unit - HttpServer - Enums') class Module { diff --git a/test/unit/extensions/http-server/extension.test.ts b/test/unit/extensions/http-server/extension.test.ts new file mode 100644 index 0000000..9c484c9 --- /dev/null +++ b/test/unit/extensions/http-server/extension.test.ts @@ -0,0 +1,175 @@ +import { suite, test } from 'mocha-typescript'; +import { HttpServerExt, LifecycleManager } from '../../../../src/extensions/http-server'; +import { HookManager, ModuleManager } from '../../../../src/core'; +import { Observable } from 'rxjs'; +import * as unit from 'unit.js'; + +import * as Hapi from 'hapi'; + +import { coreModule, EmptyModule } from '../../mocks'; + +@suite('Unit - HttpServer - Extension') +class ModuleTestSuite { + + @test('onExtensionLoad - provide module and config and must return Observable of Extension') + testOnExtensionLoad1() { + + class ServerMock { + connection(config) { + unit + .object(config) + .is({ options: undefined }); + } + } + const uglyHapi = Hapi; + const tmpServer = uglyHapi.Server; + uglyHapi['Server'] = ServerMock; + + const extInstance = new HttpServerExt(); + extInstance + .onExtensionLoad(coreModule, { options: {} }) + .subscribe( + _ => { + unit + .value(_.token) + .is(HttpServerExt); + unit + .value(_.instance) + .is(extInstance); + unit + .value(_.value) + .isInstanceOf(ServerMock); + } + ); + + uglyHapi['Server'] = tmpServer; + } + + @test('onModuleInstantiated - provide module and server and must return Observable') + testOnModuleInstantiated1() { + + class EmptyModule2 {} + const getModulesRes = [ + { token: EmptyModule }, + { token: EmptyModule2 } + ]; + const extInstance = new HttpServerExt(); + const server = { start: () => Promise.resolve() } + + const stub1 = unit + .stub(ModuleManager, 'getModules') + .withArgs(coreModule) + .returns(getModulesRes); + const stub2 = unit + .stub(extInstance, 'registerPlugin'); + stub2 + .withArgs(getModulesRes[0], server) + .returns([{}]); + stub2 + .withArgs(getModulesRes[1], server) + .returns([{}, {}]); + const stub3 = unit + .stub(LifecycleManager, 'routeLifecycle'); + const stub4 = unit + .stub(extInstance, 'instantiateLifecycle') + stub4 + .withArgs(getModulesRes[0], server) + .returns(Observable.of(getModulesRes[0])); + stub4 + .withArgs(getModulesRes[1], server) + .returns(Observable.of(getModulesRes[1])); + + extInstance + .onModuleInstantiated(coreModule, server) + .subscribe(); + + stub1.parent.restore(); + stub2.restore(); + stub3.restore(); + stub4.restore(); + } + + @test('httpHandler - provide request, reply and route and must reply 200') + testHttpHandler1() { + + const request = { + method: 'get' + }; + const reply = res => { + unit + .value(res) + .is('toto') + return { + code: c => { + unit + .value(c) + .is(200); + } + }; + }; + const stub4 = unit + .stub(HookManager, 'triggerHook') + .returns(Observable.of('toto')); + + const extInstance = new HttpServerExt(); + extInstance['httpHandler'](request, reply, {}) + + stub4.restore(); + } + + @test('httpHandler - provide request, reply and route and must reply 201') + testHttpHandler2() { + + const request = { + method: 'get' + }; + const reply = res => { + unit + .value(res) + .is('abc') + return { + code: c => { + unit + .value(c) + .is(201); + } + }; + }; + const stub4 = unit + .stub(HookManager, 'triggerHook') + .returns(Observable.of({ response: 'abc', statusCode: 201 })); + + const extInstance = new HttpServerExt(); + extInstance['httpHandler'](request, reply, {}) + + stub4.restore(); + } + + @test('httpHandler - provide request, reply and route and must reply 204') + testHttpHandler3() { + + const request = { + method: 'get' + }; + const reply = res => { + unit + .value(res) + .is(null) + return { + code: c => { + unit + .value(c) + .is(204); + } + }; + }; + const stub4 = unit + .stub(HookManager, 'triggerHook') + .returns(Observable.of(null)); + + const extInstance = new HttpServerExt(); + extInstance['httpHandler'](request, reply, {}) + + stub4.restore(); + } +} diff --git a/test/unit/hapiness.test.ts b/test/unit/hapiness.test.ts new file mode 100644 index 0000000..07aa2f5 --- /dev/null +++ b/test/unit/hapiness.test.ts @@ -0,0 +1,316 @@ +import * as util from 'util'; +import { Injectable } from '../../src/core'; +import { suite, test } from 'mocha-typescript'; +import { Hapiness, ModuleManager, HookManager, errorHandler } from '../../src/core'; +import { Observable } from 'rxjs'; +import * as unit from 'unit.js'; + +import { EmptyModule, coreModule } from './mocks'; + +@suite('Unit - Hapiness') +class TestSuite { + + @test('bootstrap - provide module and must resolve') + testBootstrap1(done) { + + const stub1 = unit + .stub(Hapiness, 'checkArg') + .withArgs(EmptyModule) + .returns(Observable.of(EmptyModule)); + const stub2 = unit + .stub(ModuleManager, 'resolve') + .withArgs(EmptyModule) + .returns(Observable.of(coreModule)); + const stub3 = unit + .stub(Hapiness, 'loadExtensions') + .withArgs(undefined, coreModule) + .returns(Observable.of([])); + + Hapiness + .bootstrap(EmptyModule) + .then(_ => { + stub1.parent.restore(); + stub2.parent.restore(); + stub3.parent.restore(); + done(); + }); + } + + @test('bootstrap - provide module and must reject') + testBootstrap2(done) { + + const stub1 = unit + .stub(Hapiness, 'checkArg') + .withArgs(EmptyModule) + .returns(Observable.throw(new Error('Oops'))); + + Hapiness + .bootstrap(EmptyModule) + .catch(_ => { + unit + .object(_) + .isInstanceOf(Error) + .hasProperty('message', 'Oops'); + + stub1.parent.restore(); + done(); + }); + } + + @test('loadExtensions - provide extensions and module and must call instantiateModule') + testLoadExtensions1() { + + class MyExt {} + const extwc = { token: MyExt, config: {} }; + const ext = { value: 123, instance: {}, token: MyExt }; + + const stub1 = unit + .stub(Hapiness, 'toExtensionWithConfig') + .withArgs(MyExt) + .returns(extwc); + const stub2 = unit + .stub(Hapiness, 'loadExtention') + .withArgs(extwc, coreModule) + .returns(Observable.of(ext)); + const stub3 = unit + .stub(Hapiness, 'instantiateModule') + .withArgs([ ext ], coreModule) + .returns(Observable.of(null)); + + Hapiness['loadExtensions']([ MyExt, null ], coreModule) + .subscribe(); + + stub1.parent.restore(); + stub2.parent.restore(); + stub3.parent.restore(); + + } + + @test('instantiateModule - provide loaded extensions and module and must call callHooks') + testInstantiateModule1() { + + class MyExt {} + const ext = { value: 123, instance: {}, token: MyExt }; + + const stub1 = unit + .stub(ModuleManager, 'instantiate') + .withArgs(coreModule, [{ provide: MyExt, useValue: 123 }]) + .returns(Observable.of(coreModule)); + const stub2 = unit + .stub(Hapiness, 'moduleInstantiated') + .withArgs(ext, coreModule) + .returns(Observable.of(null)); + const stub3 = unit + .stub(Hapiness, 'callRegister') + .withArgs(coreModule) + .returns(Observable.of(coreModule)); + const stub4 = unit + .stub(Hapiness, 'callStart') + .withArgs(coreModule) + .returns(Observable.of(null)); + + Hapiness['instantiateModule']([ ext ], coreModule) + .subscribe(); + + stub1.parent.restore(); + stub2.parent.restore(); + stub3.parent.restore(); + + } + + @test('callRegister - provide module and must call hooks') + testCallHooks1() { + + class EmptyModule2 { + onRegister() {} + } + const module = Object.assign({ instance: new EmptyModule() }, coreModule); + + const getModulesRes = [ + module, + { token: EmptyModule2, instance: new EmptyModule2() } + ]; + + const stub1 = unit + .stub(ModuleManager, 'getModules') + .withArgs(module) + .returns(getModulesRes); + const stub2 = unit + .stub(HookManager, 'hasLifecycleHook') + .withArgs('onRegister', EmptyModule2) + .returns(true); + const stub3 = unit + .stub(HookManager, 'triggerHook') + .returns(Observable.of(module)) + .withArgs('onRegister', EmptyModule2, getModulesRes[1].instance) + .returns(Observable.of(module)); + // stub3 + // .withArgs('onStart', EmptyModule, getModulesRes[0].instance, null, false) + // .returns(Observable.of(null)); + + Hapiness['callRegister'](module) + .subscribe(); + + stub1.parent.restore(); + stub2.parent.restore(); + stub3.parent.restore(); + + } + + @test('checkArg - provide module and must not throw error') + testCheckArg1(done) { + + Hapiness['checkArg'](EmptyModule) + .subscribe( + _ => done() + ); + + } + + @test('checkArg - dont provide module and must throw error') + testCheckArg2() { + + Hapiness['checkArg'](null) + .subscribe( + null, + _ => + unit + .object(_) + .isInstanceOf(Error) + .hasProperty('message', 'Bootstrap failed: no module provided') + ); + + } + + @test('checkArg - provide wrong module and must throw error') + testCheckArg3() { + + Hapiness['checkArg']('module') + .subscribe( + null, + _ => + unit + .object(_) + .isInstanceOf(Error) + .hasProperty('message', 'Bootstrap failed: module must be a function/class') + ); + + } + + @test('toExtensionWithConfig - provide extension and must return ExtensionWithConfig') + testToExtensionWithConfig1() { + + class MyExt {} + + unit + .object(Hapiness['toExtensionWithConfig'](MyExt)) + .is({ token: MyExt, config: {} }); + + unit + .object(Hapiness['toExtensionWithConfig']({ token: MyExt, config: { test: 1 } })) + .is({ token: MyExt, config: { test: 1 } }); + + } + + @test('loadExtention - provide extension and module and must return Extension') + testLoadExtention1() { + + class MyExt {} + const ext = { token: MyExt, config: {} }; + const instance = new MyExt(); + + const stub1 = unit + .stub(Reflect, 'construct') + .returns(instance); + const stub2 = unit + .stub(HookManager, 'triggerHook') + .withArgs('onExtensionLoad', MyExt, instance, [ coreModule, {} ]) + .returns(Observable.of({})); + + Hapiness['loadExtention'](ext, coreModule) + .subscribe( + _ => + unit + .value(_) + .is({}) + ) + + stub1.restore(); + stub2.parent.restore(); + + } + + @test('moduleInstantiated - provide extension and module and must return Observable') + testModuleInstantiated1() { + + class MyExt {} + const ext = { token: MyExt, instance: new MyExt(), value: 123 }; + + const stub1 = unit + .stub(HookManager, 'triggerHook') + .withArgs('onModuleInstantiated', ext.token, ext.instance, [ coreModule, ext.value ]) + .returns(Observable.of(null)); + + Hapiness['moduleInstantiated'](ext, coreModule) + .subscribe( + _ => + unit + .value(_) + .is(null) + ) + + stub1.parent.restore(); + + } + + @test('errorHandler - provide Error and must trigger onError') + testErrorHandler1() { + + class ModuleError { + onError() {} + } + const module = { token: ModuleError, instance: new ModuleError() }; + const error = new Error('Oops'); + + const stub1 = unit + .stub(HookManager, 'hasLifecycleHook') + .returns(true); + const stub2 = unit + .stub(HookManager, 'triggerHook') + .withArgs('onError', module.token, module.instance, [ error ]) + .returns(Observable.of(null)); + + Hapiness['module'] = module; + errorHandler(error); + stub1.restore(); + stub2.parent.restore(); + Hapiness['module'] = undefined; + + } + + @test('errorHandler - provide Error and must not trigger onError') + testErrorHandler2() { + + class ModuleError { + onError() {} + } + const module = { token: ModuleError, instance: new ModuleError() }; + const error = new Error('Oops'); + + const stub1 = unit + .stub(HookManager, 'hasLifecycleHook') + .returns(false); + const stub2 = unit + .stub(console, 'error') + .withArgs(error) + .returns(null); + + Hapiness['module'] = module; + errorHandler(error); + stub1.restore(); + stub2.parent.restore(); + Hapiness['module'] = undefined; + + } + +} diff --git a/test/unit/hook.test.ts b/test/unit/hook.test.ts new file mode 100644 index 0000000..5f71c25 --- /dev/null +++ b/test/unit/hook.test.ts @@ -0,0 +1,119 @@ +import * as util from 'util'; +import { Injectable } from '../../src/core'; +import { suite, test } from 'mocha-typescript'; +import { HookManager } from '../../src/core'; +import { Observable } from 'rxjs'; +import * as unit from 'unit.js'; + +import { EmptyProvider } from './mocks'; + +@suite('Unit - Hook') +class ModuleTestSuite { + + @test('hasLifecycleHook - provide hook and token and must return true') + testHasLifecycleHook1() { + + class MyToken { + test() {} + } + + unit + .bool(HookManager.hasLifecycleHook('test', MyToken)) + .isTrue(); + } + + @test('hasLifecycleHook - provide hook and token and must return false') + testHasLifecycleHook2() { + + class MyToken {} + + unit + .bool(HookManager.hasLifecycleHook('test', MyToken)) + .isFalse(); + } + + @test('triggerHook - provide hook, token and instance and must return Observable hook value') + testTriggerHook1() { + + class MyToken { + test() { return 1 } + } + + HookManager + .triggerHook('test', MyToken, new MyToken()) + .subscribe( + _ => + unit + .value(_) + .is(1) + ); + } + + @test('triggerHook - provide hook, token and instance and must return Observable') + testTriggerHook2() { + + class MyToken { + test() { return Observable.of(1) } + } + + HookManager + .triggerHook('test', MyToken, new MyToken()) + .subscribe( + _ => + unit + .value(_) + .is(1) + ); + } + + @test('triggerHook - provide hook, token and instance and must return void') + testTriggerHook3() { + + class MyToken { + test() {} + } + + HookManager + .triggerHook('test', MyToken, new MyToken()) + .isEmpty() + .subscribe( + _ => + unit + .bool(_) + .isTrue() + ); + } + + @test('triggerHook - provide hook, token and instance without hook and must throw error') + testTriggerHook4() { + + class MyToken {} + + HookManager + .triggerHook('test', MyToken, new MyToken(), null, true) + .subscribe( + null, + _ => + unit + .object(_) + .isInstanceOf(Error) + .hasProperty('message', 'Hook missing test on MyToken') + ); + } + + @test('triggerHook - provide hook, token and instance without hook and must not throw error') + testTriggerHook5() { + + class MyToken {} + + HookManager + .triggerHook('test', MyToken, new MyToken()) + .isEmpty() + .subscribe( + _ => + unit + .bool(_) + .isTrue() + ); + } +} diff --git a/test/unit/hook.unit.ts b/test/unit/hook.unit.ts deleted file mode 100644 index 266e845..0000000 --- a/test/unit/hook.unit.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { test, suite } from 'mocha-typescript'; -import * as unit from 'unit.js'; -import { Observable } from 'rxjs/Observable'; -import { HookManager } from '../../src/core'; - -@suite('Unit - Hook') -class Hook { - - @test('hasLifecycleHook') - test1() { - class TestToken { - onTest() {} - } - - unit.must(HookManager.hasLifecycleHook('onTest', TestToken)) - .is(true); - - unit.must(HookManager.hasLifecycleHook('NuLl', TestToken)) - .is(false); - - unit.must(HookManager.hasLifecycleHook(null, null)) - .is(false); - } - - @test('triggerHook - return abc') - test2(done) { - class TestToken { - onTest() { - return 'abc'; - } - } - HookManager.triggerHook('onTest', TestToken, new TestToken()) - .subscribe(_ => { - unit.must(_).equal('abc'); - done(); - }); - } - - @test('triggerHook - return Observable abc') - test3(done) { - class TestToken { - onTest() { - return Observable.of('abc'); - } - } - HookManager.triggerHook('onTest', TestToken, new TestToken()) - .subscribe(_ => { - unit.must(_).equal('abc'); - done(); - }); - } - - @test('triggerHook - hook does not exist - error') - test4(done) { - class TestToken {} - HookManager.triggerHook('onTest', TestToken, new TestToken(), null, true) - .subscribe(_ => {}, _ => { - unit.object(_) - .isInstanceOf(Error) - .hasProperty('message', 'Hook missing onTest on TestToken'); - done(); - }); - } - - @test('triggerHook - hook does not exist - no error') - test5(done) { - class TestToken {} - HookManager.triggerHook('onTest', TestToken, new TestToken()) - .subscribe(_ => { - unit.must(_).equal(undefined); - done(); - }); - } - - @test('triggerHook - no token/instance') - test6() { - unit.error(HookManager.triggerHook) - .is(new Error('Cannot trigger without token/instance')); - } -} diff --git a/test/unit/metadata.unit.ts b/test/unit/metadata.test.ts similarity index 67% rename from test/unit/metadata.unit.ts rename to test/unit/metadata.test.ts index 5a40747..17567d7 100644 --- a/test/unit/metadata.unit.ts +++ b/test/unit/metadata.test.ts @@ -1,6 +1,7 @@ import { suite, test } from 'mocha-typescript'; import * as unit from 'unit.js'; -import { HapinessModule, extractMetadataByDecorator, extractMetadata } from '../../src/core'; +import { extractMetadataByDecorator, extractMetadata, extractMetadatas } from '../../src/core/metadata'; +import { HapinessModule } from '../../src/core/decorators'; @suite('Unit - Metadata') class Metadata { @@ -17,6 +18,16 @@ class Metadata { } + @test('extractMetadatas') + test3() { + + class TestToken {} + + unit.array(extractMetadatas(TestToken)) + .is([]); + + } + @test('extractMetadataByDecorator') test2() { @HapinessModule({ diff --git a/test/unit/mocks.ts b/test/unit/mocks.ts new file mode 100644 index 0000000..3fa6ce1 --- /dev/null +++ b/test/unit/mocks.ts @@ -0,0 +1,45 @@ +import 'reflect-metadata'; + +import { CoreModule } from '../../src/core/interfaces' +import { ModuleLevel } from '../../src/core/enums'; +import { InjectionToken, HapinessModule, Lib } from '../../src/core/decorators'; + +export class EmptyModule { + onStart() {} +} +export class EmptyProvider {} +export const coreModule: CoreModule = { + token: EmptyModule, + name: EmptyModule.name, + version: '1', + level: ModuleLevel.ROOT +} +export const InjToken = new InjectionToken('token'); + +@HapinessModule({ + version: '123' +}) +export class ModuleWithMetadata {} + + +@HapinessModule({ + version: '123', + imports: [ ModuleWithMetadata ] +}) +export class ModuleWithMetadataWithChild {} + +@HapinessModule({ + version: '123', + exports: [ EmptyProvider ], + providers: [{ provide: InjToken, useValue: 0 }] +}) +export class ModuleWithMetadataExportProvider {} + +@HapinessModule({ + version: '123', + imports: [ ModuleWithMetadataExportProvider ] +}) +export class ModuleWithMetadataWithChildThatExportProvider {} + +@Lib() +export class EmptyLib {} diff --git a/test/unit/module.test.ts b/test/unit/module.test.ts new file mode 100644 index 0000000..7a38b7a --- /dev/null +++ b/test/unit/module.test.ts @@ -0,0 +1,358 @@ +import { suite, test } from 'mocha-typescript'; +import { ModuleManager } from '../../src/core/module'; +import { ModuleLevel } from '../../src/core/enums'; +import { HapinessModule, Lib } from '../../src/core/decorators'; +import { DependencyInjection } from '../../src/core/di'; +import { Observable } from 'rxjs'; +import * as unit from 'unit.js'; + +import { extractMetadataByDecorator } from '../../src/core/metadata'; + +import { + EmptyProvider, + EmptyModule, + coreModule, + InjToken, + ModuleWithMetadata, + ModuleWithMetadataWithChild, + ModuleWithMetadataWithChildThatExportProvider, + EmptyLib +} from './mocks'; + +@suite('Unit - Module') +class ModuleTestSuite { + + @test('toCoreProvider - provide Class and must return CoreProvide') + testToCoreProvider1() { + + unit + .value(ModuleManager.toCoreProvider(EmptyProvider)) + .is({ provide: EmptyProvider, useClass: EmptyProvider }); + + } + + @test('toCoreProvider - provide CoreProvide and must return CoreProvide') + testToCoreProvider2() { + + unit + .value(ModuleManager.toCoreProvider({ provide: EmptyProvider, useClass: EmptyProvider })) + .is({ provide: EmptyProvider, useClass: EmptyProvider }); + + } + + @test('coreModuleParentConfigProviders - with no parent must return the module with same providers') + testCoreModuleParentConfigProviders1() { + + unit + .object(ModuleManager['coreModuleParentConfigProviders'](coreModule).providers) + .is([]); + + } + + @test('coreModuleParentConfigProviders - with parent must return the module with InjectionToken providers') + testCoreModuleParentConfigProviders2() { + + const module = Object.assign({ + parent: { + providers: [ { provide: InjToken, useClass: EmptyProvider } ] + } + }, coreModule); + + unit + .object(ModuleManager['coreModuleParentConfigProviders'](module).providers) + .is([{ provide: InjToken, useClass: EmptyProvider }]); + + } + + @test('metadataToCoreModule - must return an Observable of CoreModule whithout parent') + testMetadataToCoreModule1() { + + const metadata = { + version: '1', + }; + const cmwp = { + module: EmptyModule, + providers: [ { provide: InjToken, useClass: EmptyProvider } ] + }; + + ModuleManager['metadataToCoreModule'](metadata, cmwp) + .subscribe( + _ => unit + .value(_) + .is({ + parent: undefined, + token: EmptyModule, + name: 'EmptyModule', + version: '1', + exports: [], + declarations: [], + providers: [ { provide: InjToken, useClass: EmptyProvider } ], + level: ModuleLevel.ROOT + }) + ); + + } + + @test('metadataToCoreModule - must return an Observable of CoreModule with ROOT parent') + testMetadataToCoreModule2() { + + const metadata = { + version: '1', + }; + const cmwp = { + module: EmptyModule, + providers: [ { provide: InjToken, useClass: EmptyProvider } ] + }; + + ModuleManager['metadataToCoreModule'](metadata, cmwp, coreModule) + .subscribe( + _ => unit + .value(_) + .is({ + parent: coreModule, + token: EmptyModule, + name: 'EmptyModule', + version: '1', + exports: [], + declarations: [], + providers: [ { provide: InjToken, useClass: EmptyProvider } ], + level: ModuleLevel.PRIMARY + }) + ); + + } + + @test('metadataToCoreModule - must return an Observable of CoreModule with PRIMARY parent') + testMetadataToCoreModule3() { + + const metadata = { + version: '1', + }; + const cmwp = { + module: EmptyModule, + providers: [ { provide: InjToken, useClass: EmptyProvider } ] + }; + const module = Object.assign({}, coreModule, { level: ModuleLevel.PRIMARY }); + + ModuleManager['metadataToCoreModule'](metadata, cmwp, module) + .subscribe( + _ => unit + .value(_) + .is({ + parent: module, + token: EmptyModule, + name: 'EmptyModule', + version: '1', + exports: [], + declarations: [], + providers: [ { provide: InjToken, useClass: EmptyProvider } ], + level: ModuleLevel.SECONDARY + }) + ); + + } + + @test('extractMetadata - must return an Observable of HapinessModule') + testExtractMetadata1() { + + const stub = unit + .stub(require('../../src/core/metadata'), 'extractMetadataByDecorator') + .withArgs(ModuleWithMetadata) + .returns({ version: '123' }); + + ModuleManager['extractMetadata'](ModuleWithMetadata) + .subscribe( + _ => unit + .value(_) + .is({ version: '123' }) + ); + stub.parent.restore(); + + } + + @test('extractMetadata - must return an Observable with error') + testExtractMetadata2() { + + const stub = unit + .stub(require('../../src/core/metadata'), 'extractMetadataByDecorator') + .withArgs(null).returns(null) + .withArgs(EmptyModule).returns(null); + + ModuleManager['extractMetadata'](null) + .subscribe( + _ => {}, + _ => unit + .object(_) + .isInstanceOf(Error) + .hasProperty('message', 'Module \'null\' resolution failed: No metadata') + ); + + ModuleManager['extractMetadata'](EmptyModule) + .subscribe( + _ => {}, + _ => unit + .object(_) + .isInstanceOf(Error) + .hasProperty('message', 'Module \'EmptyModule\' resolution failed: No metadata') + ); + stub.parent.restore(); + + } + + @test('toCoreModuleWithProviders - provide a Class and must return an CoreModuleWithProviders') + testToCoreModuleWithProviders1() { + + unit + .value(ModuleManager['toCoreModuleWithProviders'](EmptyModule)) + .is({ module: EmptyModule, providers: [] }); + + } + + @test('toCoreModuleWithProviders - provide a CoreModuleWithProviders and must return an CoreModuleWithProviders') + testToCoreModuleWithProviders2() { + + unit + .value(ModuleManager['toCoreModuleWithProviders']({ module: EmptyModule, providers: [] })) + .is({ module: EmptyModule, providers: [] }); + + } + + @test('resolution - provide a Class module with metadata that got 1 child and ' + + 'must return an Observable of CoreModule with a child module') + testResolution1() { + + ModuleManager['resolution'](ModuleWithMetadataWithChild) + .subscribe( + _ => { + unit + .object(_) + .hasProperty('name', 'ModuleWithMetadataWithChild'); + + unit + .array(_.modules) + .hasLength(1); + + unit + .object(_.modules.pop()) + .hasProperty('name', 'ModuleWithMetadata'); + } + ); + + } + + @test('collectProviders - provide a CoreModule and must return array of CoreProvide') + testCollectProviders1() { + + const cmodule = Object.assign({}, coreModule, { providers: [{ provide: EmptyModule, useClass: EmptyModule }] }); + const stub = unit + .stub(ModuleManager, 'extractExportedProviders') + .withArgs(cmodule) + .returns([]); + + unit + .array(ModuleManager['collectProviders'](cmodule)) + .hasLength(1) + .is([{ provide: EmptyModule, useClass: EmptyModule }]); + + stub.parent.restore(); + + } + + @test('extractExportedProviders - provide a CoreModule and must return array of CoreProvide exported by children') + testExtractExportedProviders1() { + + ModuleManager + .resolve(ModuleWithMetadataWithChildThatExportProvider) + .subscribe( + _ => { + unit + .array(ModuleManager['extractExportedProviders'](_)) + .hasLength(2) + .is([ + { provide: EmptyProvider, useClass: EmptyProvider }, + { provide: InjToken, useValue: 0 } + ]); + } + ); + + } + + @test('instantiation - provide a CoreModule and must return an Observable of CoreModule with di and instance') + testInstantiation1() { + + ModuleManager + .resolve(ModuleWithMetadataWithChild) + .flatMap(_ => ModuleManager.instantiate(_)) + .subscribe( + _ => { + unit + .object(_) + .hasProperty('di'); + unit + .object(_.instance) + .isInstanceOf(ModuleWithMetadataWithChild); + } + ); + + } + + @test('getModules - provide a CoreModule and must return array of module') + testGetModules1() { + + const module = Object.assign({}, coreModule, { modules: {} }); + + unit + .array(ModuleManager.getModules(module)) + .is([ module, {} ]); + + } + + @test('instantiateLibs - provide a CoreModule and must return an Observable of CoreModule') + testInstantiateLibs1() { + + const module = Object.assign({}, coreModule, { declarations: [ EmptyLib ], di: { stub: true } }); + const stub = unit + .stub(DependencyInjection, 'instantiateComponent') + .withArgs(EmptyLib, { stub: true }) + .returns(Observable.of(null)) + + ModuleManager['instantiateLibs'](module) + .subscribe(_ => + unit + .object(_) + .is(module) + ); + + stub.parent.restore(); + + } + + @test('instantiateLibs - provide a CoreModule and must thrown an error') + testInstantiateLibs2() { + + @Lib() + class LibWithError { + constructor() { + throw new Error('Oops'); + } + } + + const module = Object.assign({}, coreModule, { declarations: [ EmptyLib ], di: { stub: true } }); + const stub = unit + .stub(DependencyInjection, 'instantiateComponent') + .withArgs(EmptyLib, { stub: true }) + .returns(Observable.of(null)) + + ModuleManager['instantiateLibs'](module) + .subscribe( + null, + _ => unit + .object(_) + .isInstanceOf(Error) + .hasProperty('message', 'Oops') + ); + + stub.parent.restore(); + + } +} diff --git a/test/unit/module.unit.ts b/test/unit/module.unit.ts deleted file mode 100644 index 1a3c892..0000000 --- a/test/unit/module.unit.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { suite, test } from 'mocha-typescript'; -import * as unit from 'unit.js'; -import { ModuleManager, ModuleLevel, HapinessModule, InjectionToken } from '../../src/core'; - -@suite('Unit - Module') -class Module { - - @test('findNestedModule') - test1() { - - const cm = { - name: 'TestModule', - modules: [{ - name: 'SubTestModule', - parent: this - }] - } - - unit.must(ModuleManager.findNestedModule('TestModule', cm)) - .equal(undefined); - - unit.must(ModuleManager.findNestedModule('SubTestModule', cm).name) - .is('SubTestModule'); - - } - - @test('getElements') - test2() { - - class Comp1 {} - class Comp2 {} - class Comp3 {} - - const cm = { - name: 'TestModule', - declarations: [ Comp1, Comp2 ], - modules: [{ - name: 'SubTestModule', - declarations: [ Comp3 ] - }] - } - - unit.array(ModuleManager.getElements(cm, 'providers')) - .is([]); - - unit.array(ModuleManager.getElements(cm, 'declarations')) - .is([ Comp3, Comp1, Comp2 ]); - - unit.error(ModuleManager.getElements, /You need to provide the element you want to get/); - - } - - @test('getModules') - test3() { - - const cm = { - name: 'TestModule', - modules: [{ - name: 'SubTestModule', - modules: [{ - name: 'SubSubModule1' - }, { - name: 'SubSubModule2' - }] - }] - } - - unit.array(ModuleManager.getModules(cm)) - .hasLength(4); - - } - - @test('coreModuleFromMetadata') - test4() { - - class ModuleTest {} - class ServiceTest {} - - const data = { - version: '1.0.0', - exports: [], - declarations: [ 'declaration' ] - }; - - const module = { - module: ModuleTest, - providers: [ ServiceTest ] - }; - - unit.object(ModuleManager['coreModuleFromMetadata'](data, module)) - .is({ - parent: undefined, - token: ModuleTest, - name: 'ModuleTest', - version: '1.0.0', - exports: [], - declarations: [ 'declaration' ], - providers: [{ provide: ServiceTest, useClass: ServiceTest }], - level: ModuleLevel.ROOT - }); - - } - - @test('metadataFromModule') - test5() { - - @HapinessModule({ - version: 'xxx', - }) - class ModuleTest {} - - unit.object(ModuleManager['metadataFromModule'](ModuleTest)) - .contains({ - version: 'xxx', - declarations: undefined, - providers: undefined, - imports: undefined, - exports: undefined - }); - - } - - @test('collectProviders - extractExportedProviders') - test6() { - - class Service1 {} - class Service2 {} - class Service3 {} - const token = new InjectionToken('config'); - - const module = { - providers: [ Service1 ], - modules: [{ - exports: [ Service3 ], - providers: [{ provide: token, useValue: 'test' }] - }] - }; - - const providers = [ - { provide: Service2, useClass: Service2 } - ]; - - unit.array(ModuleManager['collectProviders'](module, providers)) - .is([ - Service1, - { provide: Service2, useClass: Service2 }, - Service3, - { provide: token, useValue: 'test' } - ]); - - } - -} diff --git a/yarn.lock b/yarn.lock index 9ef8155..a5e5cb0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,25 +2,25 @@ # yarn lockfile v1 -"@types/boom@*", "@types/boom@^4.3.5": - version "4.3.5" - resolved "https://registry.yarnpkg.com/@types/boom/-/boom-4.3.5.tgz#9ccca31e08c989bce483bd76b2de798526c608f5" +"@types/boom@*": + version "4.3.7" + resolved "https://registry.yarnpkg.com/@types/boom/-/boom-4.3.7.tgz#2771d7b1fd7df70c6860ed30ec6752feaca9f3de" "@types/catbox@*": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@types/catbox/-/catbox-7.1.1.tgz#170e50c35f047163e709c43e8bab551548a7d429" + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/catbox/-/catbox-7.1.3.tgz#895ee7d335fda7e5af688b21e8b9b5e84e30dd39" dependencies: "@types/boom" "*" -"@types/fs-extra@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-3.0.3.tgz#1d66eb670ebf657e57c0fda014df340c19d8aa0c" +"@types/fs-extra@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.0.tgz#1dd742ad5c9bce308f7a52d02ebc01421bc9102f" dependencies: "@types/node" "*" -"@types/hapi@^16.1.6": - version "16.1.6" - resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-16.1.6.tgz#a55e0b201e69ca74a147776b3e1a464aa7b14c76" +"@types/hapi@^16.1.9": + version "16.1.9" + resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-16.1.9.tgz#0748fa17dffdd59c97e6e2ef0c4343afc4261d64" dependencies: "@types/boom" "*" "@types/catbox" "*" @@ -34,23 +34,23 @@ version "4.1.3" resolved "https://registry.yarnpkg.com/@types/hoek/-/hoek-4.1.3.tgz#d1982d48fb0d2a0e5d7e9d91838264d8e428d337" -"@types/joi@*", "@types/joi@^10.4.0": - version "10.4.0" - resolved "https://registry.yarnpkg.com/@types/joi/-/joi-10.4.0.tgz#a4de40cdf2462c7e1c83eb47ca3e19177bcc2ae8" +"@types/joi@*", "@types/joi@^10.4.1": + version "10.4.1" + resolved "https://registry.yarnpkg.com/@types/joi/-/joi-10.4.1.tgz#30e987649cede8d2a78829489857e969a81e1e99" "@types/mime-db@*": version "1.27.0" resolved "https://registry.yarnpkg.com/@types/mime-db/-/mime-db-1.27.0.tgz#9bc014a1fd1fdf47649c1a54c6dd7966b8284792" "@types/mimos@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/mimos/-/mimos-3.0.0.tgz#20356e7381e9529c2a7e764c798be5a48cdeff96" + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mimos/-/mimos-3.0.1.tgz#59d96abe1c9e487e7463fe41e8d86d76b57a441a" dependencies: "@types/mime-db" "*" -"@types/node@*", "@types/node@^8.0.13": - version "8.0.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.13.tgz#530f0f9254209b0335bf5cc6387822594ef47093" +"@types/node@*", "@types/node@^8.0.25": + version "8.0.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.25.tgz#66ecaf4df93f5281b48427ee96fbcdfc4f0cdce1" "@types/podium@*": version "1.0.0" @@ -76,7 +76,7 @@ abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" -accept@2.x.x: +accept@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/accept/-/accept-2.1.4.tgz#887af54ceee5c7f4430461971ec400c61d09acbb" dependencies: @@ -95,7 +95,7 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" -ammo@2.x.x: +ammo@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/ammo/-/ammo-2.0.4.tgz#bf80aab211698ea78f63ef5e7f113dd5d9e8917f" dependencies: @@ -111,10 +111,10 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" ansi-styles@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.1.0.tgz#09c202d5c917ec23188caa5c9cb9179cd9547750" + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" dependencies: - color-convert "^1.0.0" + color-convert "^1.9.0" append-transform@^0.4.0: version "0.4.0" @@ -171,28 +171,28 @@ aws4@^1.2.1: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" b64@3.x.x: - version "3.0.2" - resolved "https://registry.yarnpkg.com/b64/-/b64-3.0.2.tgz#7a9d60466adf7b8de114cbdf651a5fdfcc90894d" + version "3.0.3" + resolved "https://registry.yarnpkg.com/b64/-/b64-3.0.3.tgz#36afeee0d9345f046387ce6de8a6702afe5bb56e" -babel-code-frame@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: - chalk "^1.1.0" + chalk "^1.1.3" esutils "^2.0.2" - js-tokens "^3.0.0" + js-tokens "^3.0.2" babel-generator@^6.18.0: - version "6.25.0" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc" + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" dependencies: babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-types "^6.25.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" detect-indent "^4.0.0" jsesc "^1.3.0" - lodash "^4.2.0" - source-map "^0.5.0" + lodash "^4.17.4" + source-map "^0.5.6" trim-right "^1.0.1" babel-messages@^6.23.0: @@ -201,49 +201,49 @@ babel-messages@^6.23.0: dependencies: babel-runtime "^6.22.0" -babel-runtime@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" +babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: core-js "^2.4.0" - regenerator-runtime "^0.10.0" + regenerator-runtime "^0.11.0" babel-template@^6.16.0: - version "6.25.0" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071" + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.25.0" - babel-types "^6.25.0" - babylon "^6.17.2" - lodash "^4.2.0" + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" -babel-traverse@^6.18.0, babel-traverse@^6.25.0: - version "6.25.0" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" +babel-traverse@^6.18.0, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" dependencies: - babel-code-frame "^6.22.0" + babel-code-frame "^6.26.0" babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-types "^6.25.0" - babylon "^6.17.2" - debug "^2.2.0" - globals "^9.0.0" - invariant "^2.2.0" - lodash "^4.2.0" - -babel-types@^6.18.0, babel-types@^6.25.0: - version "6.25.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" - dependencies: - babel-runtime "^6.22.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.18.0, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" esutils "^2.0.2" - lodash "^4.2.0" - to-fast-properties "^1.0.1" + lodash "^4.17.4" + to-fast-properties "^1.0.3" -babylon@^6.17.2, babylon@^6.17.4: - version "6.17.4" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" +babylon@^6.17.4, babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" balanced-match@^1.0.0: version "1.0.0" @@ -265,9 +265,9 @@ boom@2.x.x: dependencies: hoek "2.x.x" -boom@5.x.x: - version "5.1.0" - resolved "https://registry.yarnpkg.com/boom/-/boom-5.1.0.tgz#0308fa8e924cd6d42d9c3bf4883bdc98f0e71df8" +boom@5.x.x, boom@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" dependencies: hoek "4.x.x" @@ -286,7 +286,7 @@ builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" -call@4.x.x: +call@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/call/-/call-4.0.2.tgz#df76f5f51ee8dd48b856ac8400f7e69e6d7399c4" dependencies: @@ -305,13 +305,13 @@ caseless@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" -catbox-memory@2.x.x: +catbox-memory@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/catbox-memory/-/catbox-memory-2.0.4.tgz#433e255902caf54233d1286429c8f4df14e822d5" dependencies: hoek "4.x.x" -catbox@7.x.x: +catbox@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/catbox/-/catbox-7.1.5.tgz#c56f7e8e9555d27c0dc038a96ef73e57d186bb1f" dependencies: @@ -326,7 +326,7 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -337,8 +337,8 @@ chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: supports-color "^2.0.0" chalk@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.0.1.tgz#dbec49436d2ae15f536114e76d14656cdbc0f44d" + version "2.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" dependencies: ansi-styles "^3.1.0" escape-string-regexp "^1.0.5" @@ -364,7 +364,7 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -color-convert@^1.0.0: +color-convert@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" dependencies: @@ -409,8 +409,8 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" content@3.x.x: - version "3.0.4" - resolved "https://registry.yarnpkg.com/content/-/content-3.0.4.tgz#ca3dde04480f12519b71526ec44bd488ddfb3fef" + version "3.0.5" + resolved "https://registry.yarnpkg.com/content/-/content-3.0.5.tgz#9e147dc7c838c4de9483096845ddb4de455ec509" dependencies: boom "5.x.x" @@ -419,10 +419,10 @@ cookiejar@2.0.1: resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.0.1.tgz#3d12752f6adf68a892f332433492bd5812bb668f" core-js@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + version "2.5.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.0.tgz#569c050918be6486b3837552028ae0466b717086" -core-util-is@~1.0.0: +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -442,7 +442,7 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" -cryptiles@3.x.x: +cryptiles@3.x.x, cryptiles@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" dependencies: @@ -454,17 +454,17 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -debug@2, debug@^2.2.0, debug@^2.6.3, debug@^2.6.8: +debug@2, debug@2.6.8, debug@^2.2.0, debug@^2.6.3, debug@^2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" -debug@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" +debug@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.0.1.tgz#0564c612b521dc92d9f2988f0549e34f9c98db64" dependencies: - ms "0.7.2" + ms "2.0.0" decamelize@^1.0.0, decamelize@^1.1.1: version "1.2.0" @@ -534,9 +534,9 @@ extend@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" -extsprintf@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" +extsprintf@1.3.0, extsprintf@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" fileset@^2.0.2: version "2.0.3" @@ -582,9 +582,9 @@ formidable@1.0.14: version "1.0.14" resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.0.14.tgz#2b3f4c411cbb5fdd695c44843e2a23514a43231a" -fs-extra@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.0.tgz#414fb4ca2d2170ba0014159d3a8aec3303418d9e" +fs-extra@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.1.tgz#7fc0c6c8957f983f57f306a24e5b9ddd8d0dd880" dependencies: graceful-fs "^4.1.2" jsonfile "^3.0.0" @@ -636,7 +636,7 @@ glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^9.0.0: +globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -662,28 +662,28 @@ handlebars@^4.0.3: optionalDependencies: uglify-js "^2.6" -hapi@^16.4.3: - version "16.4.3" - resolved "https://registry.yarnpkg.com/hapi/-/hapi-16.4.3.tgz#be4daaf2dcdbee97957ce503061b09765078aa05" - dependencies: - accept "2.x.x" - ammo "2.x.x" - boom "5.x.x" - call "4.x.x" - catbox "7.x.x" - catbox-memory "2.x.x" - cryptiles "3.x.x" - heavy "4.x.x" - hoek "4.x.x" - iron "4.x.x" - items "2.x.x" - joi "10.x.x" - mimos "3.x.x" - podium "1.x.x" - shot "3.x.x" - statehood "5.x.x" - subtext "4.x.x" - topo "2.x.x" +hapi@^16.5.2: + version "16.5.2" + resolved "https://registry.yarnpkg.com/hapi/-/hapi-16.5.2.tgz#d1dadf33721c6ac3aaa905ce086d9c7ffb883092" + dependencies: + accept "^2.1.4" + ammo "^2.0.4" + boom "^5.2.0" + call "^4.0.2" + catbox "^7.1.5" + catbox-memory "^2.0.4" + cryptiles "^3.1.2" + heavy "^4.0.4" + hoek "^4.2.0" + iron "^4.0.5" + items "^2.1.1" + joi "^10.6.0" + mimos "^3.0.3" + podium "^1.3.0" + shot "^3.4.2" + statehood "^5.0.3" + subtext "^5.0.0" + topo "^2.0.2" har-validator@~2.0.6: version "2.0.6" @@ -717,7 +717,7 @@ hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" -heavy@4.x.x: +heavy@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/heavy/-/heavy-4.0.4.tgz#36c91336c00ccfe852caa4d153086335cd2f00e9" dependencies: @@ -729,9 +729,9 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -hoek@4.x.x: - version "4.1.1" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.1.1.tgz#9cc573ffba2b7b408fb5e9c2a13796be94cddce9" +hoek@4.x.x, hoek@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" hosted-git-info@^2.1.4: version "2.5.0" @@ -760,7 +760,7 @@ inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" -invariant@^2.2.0: +invariant@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" dependencies: @@ -770,7 +770,7 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -iron@4.x.x: +iron@4.x.x, iron@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/iron/-/iron-4.0.5.tgz#4f042cceb8b9738f346b59aa734c83a89bc31428" dependencies: @@ -805,8 +805,8 @@ is-fullwidth-code-point@^1.0.0: number-is-nan "^1.0.0" is-my-json-valid@^2.12.4: - version "2.16.0" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" dependencies: generate-function "^2.0.0" generate-object-property "^1.1.0" @@ -842,17 +842,17 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" istanbul-api@^1.1.0-alpha: - version "1.1.11" - resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.11.tgz#fcc0b461e2b3bda71e305155138238768257d9de" + version "1.1.13" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.13.tgz#7197f64413600ebdfec6347a2dc3d4e03f97ed5a" dependencies: async "^2.1.4" fileset "^2.0.2" istanbul-lib-coverage "^1.1.1" istanbul-lib-hook "^1.0.7" - istanbul-lib-instrument "^1.7.4" + istanbul-lib-instrument "^1.7.5" istanbul-lib-report "^1.1.1" istanbul-lib-source-maps "^1.2.1" - istanbul-reports "^1.1.1" + istanbul-reports "^1.1.2" js-yaml "^3.7.0" mkdirp "^0.5.1" once "^1.4.0" @@ -867,9 +867,9 @@ istanbul-lib-hook@^1.0.7: dependencies: append-transform "^0.4.0" -istanbul-lib-instrument@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.4.tgz#e9fd920e4767f3d19edc765e2d6b3f5ccbd0eea8" +istanbul-lib-instrument@^1.7.5: + version "1.7.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.5.tgz#adb596f8f0cb8b95e739206351a38a586af21b1e" dependencies: babel-generator "^6.18.0" babel-template "^6.16.0" @@ -898,9 +898,9 @@ istanbul-lib-source-maps@^1.2.1: rimraf "^2.6.1" source-map "^0.5.3" -istanbul-reports@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.1.tgz#042be5c89e175bc3f86523caab29c014e77fee4e" +istanbul-reports@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.2.tgz#0fb2e3f6aa9922bd3ce45d05d8ab4d5e8e07bd4f" dependencies: handlebars "^4.0.3" @@ -917,11 +917,11 @@ istanbul@^1.1.0-alpha.1: which "^1.1.1" wordwrap "^1.0.0" -items@2.x.x: +items@2.x.x, items@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/items/-/items-2.1.1.tgz#8bd16d9c83b19529de5aea321acaada78364a198" -joi@10.x.x: +joi@10.x.x, joi@^10.6.0: version "10.6.0" resolved "https://registry.yarnpkg.com/joi/-/joi-10.6.0.tgz#52587f02d52b8b75cdb0c74f0b164a191a0e1fc2" dependencies: @@ -930,7 +930,7 @@ joi@10.x.x: items "2.x.x" topo "2.x.x" -js-tokens@^3.0.0: +js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -942,8 +942,8 @@ js-yaml@3.6.1: esprima "^2.6.0" js-yaml@3.x, js-yaml@^3.7.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.0.tgz#4ffbbf25c2ac963b8299dc74da7e3740de1c18ce" + version "3.9.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.1.tgz#08775cebdfdd359209f0d2acd383c8f86a6904a0" dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -979,13 +979,13 @@ jsonpointer@^4.0.0: resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" jsprim@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" dependencies: assert-plus "1.0.0" - extsprintf "1.0.2" + extsprintf "1.3.0" json-schema "0.2.3" - verror "1.3.6" + verror "1.10.0" kind-of@^3.0.2: version "3.2.2" @@ -1072,7 +1072,7 @@ lodash@^3.7.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" -lodash@^4.14.0, lodash@^4.2.0: +lodash@^4.14.0, lodash@^4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -1107,24 +1107,24 @@ methods@1.x: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" mime-db@1.x.x: + version "1.30.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + +mime-db@~1.29.0: version "1.29.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878" -mime-db@~1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" - mime-types@^2.1.12, mime-types@~2.1.7: - version "2.1.15" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" + version "2.1.16" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.16.tgz#2b858a52e5ecd516db897ac2be87487830698e23" dependencies: - mime-db "~1.27.0" + mime-db "~1.29.0" mime@1.2.11, mime@~1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" -mimos@3.x.x: +mimos@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/mimos/-/mimos-3.0.3.tgz#b9109072ad378c2b72f6a0101c43ddfb2b36641f" dependencies: @@ -1162,13 +1162,13 @@ mocha-typescript@^1.1.7: chalk "^1.1.3" yargs "^6.5.0" -mocha@^3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.4.2.tgz#d0ef4d332126dbf18d0d640c9b382dd48be97594" +mocha@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.0.tgz#1328567d2717f997030f8006234bce9b8cd72465" dependencies: browser-stdout "1.3.0" commander "2.9.0" - debug "2.6.0" + debug "2.6.8" diff "3.2.0" escape-string-regexp "1.0.5" glob "7.1.1" @@ -1178,10 +1178,6 @@ mocha@^3.4.2: mkdirp "0.5.1" supports-color "3.1.2" -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -1301,9 +1297,9 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" -podium@1.x.x: - version "1.2.5" - resolved "https://registry.yarnpkg.com/podium/-/podium-1.2.5.tgz#87c566c2f0365bcf0a1ec7602c4d01948cdd2ad5" +podium@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/podium/-/podium-1.3.0.tgz#3c490f54d16f10f5260cbe98641f1cb733a8851c" dependencies: hoek "4.x.x" items "2.x.x" @@ -1353,9 +1349,9 @@ reflect-metadata@^0.1.10: version "0.1.10" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.10.tgz#b4f83704416acad89988c9b15635d47e03b9344a" -regenerator-runtime@^0.10.0: - version "0.10.5" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" +regenerator-runtime@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" repeat-string@^1.5.2: version "1.6.1" @@ -1401,8 +1397,8 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" resolve@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" + version "1.4.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" dependencies: path-parse "^1.0.5" @@ -1418,9 +1414,9 @@ rimraf@^2.6.1: dependencies: glob "^7.0.5" -rxjs@^5.4.2: - version "5.4.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.2.tgz#2a3236fcbf03df57bae06fd6972fd99e5c08fcf7" +rxjs@^5.4.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.3.tgz#0758cddee6033d68e0fd53676f0f3596ce3d483f" dependencies: symbol-observable "^1.0.1" @@ -1433,14 +1429,14 @@ samsam@~1.1: resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" "semver@2 || 3 || 4 || 5", semver@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" -shot@3.x.x: +shot@^3.4.2: version "3.4.2" resolved "https://registry.yarnpkg.com/shot/-/shot-3.4.2.tgz#1e5c3f6f2b26649adc42f7eb350214a5a0291d67" dependencies: @@ -1487,8 +1483,8 @@ sntp@1.x.x: hoek "2.x.x" source-map-support@^0.4.0: - version "0.4.15" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1" + version "0.4.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.16.tgz#16fecf98212467d017d586a2af68d628b9421cd8" dependencies: source-map "^0.5.6" @@ -1498,9 +1494,9 @@ source-map@^0.4.4: dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" +source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" spdx-correct@~1.0.0: version "1.0.2" @@ -1534,9 +1530,9 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -statehood@5.x.x: - version "5.0.2" - resolved "https://registry.yarnpkg.com/statehood/-/statehood-5.0.2.tgz#c6b3baa16ed8b121d3f09a3ffa85e22195a7f2a9" +statehood@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/statehood/-/statehood-5.0.3.tgz#c07a75620db5379b60d2edd47f538002a8ac7dd6" dependencies: boom "5.x.x" cryptiles "3.x.x" @@ -1581,9 +1577,9 @@ strip-json-comments@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" -subtext@4.x.x: - version "4.4.1" - resolved "https://registry.yarnpkg.com/subtext/-/subtext-4.4.1.tgz#2fcec945de429283c3d18b151ff0fa1f1b87aec9" +subtext@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/subtext/-/subtext-5.0.0.tgz#9c3f083018bb1586b167ad8cfd87083f5ccdfe0f" dependencies: boom "5.x.x" content "3.x.x" @@ -1631,8 +1627,8 @@ supports-color@^3.1.2: has-flag "^1.0.0" supports-color@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.2.0.tgz#ad986dc7eb2315d009b4d77c8169c2231a684037" + version "4.2.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.2.1.tgz#65a4bb2631e90e02420dba5554c375a4754bb836" dependencies: has-flag "^2.0.0" @@ -1640,11 +1636,11 @@ symbol-observable@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" -to-fast-properties@^1.0.1: +to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" -topo@2.x.x: +topo@2.x.x, topo@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/topo/-/topo-2.0.2.tgz#cd5615752539057c0dc0491a621c3bc6fbe1d182" dependencies: @@ -1660,9 +1656,9 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -ts-node@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-3.2.0.tgz#9814f0c0141784900cf12fef1197ad4b7f4d23d1" +ts-node@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-3.3.0.tgz#c13c6a3024e30be1180dd53038fc209289d4bf69" dependencies: arrify "^1.0.0" chalk "^2.0.0" @@ -1672,7 +1668,7 @@ ts-node@^3.2.0: mkdirp "^0.5.1" source-map-support "^0.4.0" tsconfig "^6.0.0" - v8flags "^2.0.11" + v8flags "^3.0.0" yn "^2.0.0" tsconfig@^6.0.0: @@ -1687,8 +1683,8 @@ tslib@^1.7.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec" tslint@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.5.0.tgz#10e8dab3e3061fa61e9442e8cee3982acf20a6aa" + version "5.7.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.7.0.tgz#c25e0d0c92fa1201c2bc30e844e08e682b4f3552" dependencies: babel-code-frame "^6.22.0" colors "^1.1.2" @@ -1699,11 +1695,11 @@ tslint@^5.5.0: resolve "^1.3.2" semver "^5.3.0" tslib "^1.7.1" - tsutils "^2.5.1" + tsutils "^2.8.1" -tsutils@^2.5.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.7.1.tgz#411a0e9466525a2b2869260a55620d7292155e24" +tsutils@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.1.tgz#3771404e7ca9f0bedf5d919a47a4b1890a68efff" dependencies: tslib "^1.7.1" @@ -1721,9 +1717,9 @@ typedarray-to-buffer@^3.1.2: dependencies: is-typedarray "^1.0.0" -typescript@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc" +typescript@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.1.tgz#ce7cc93ada3de19475cc9d17e3adea7aee1832aa" uglify-js@^2.6: version "2.8.29" @@ -1751,8 +1747,8 @@ unit.js@^2.0.0: supertest ">= 0.15.0 < 1.0.0" universalify@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.0.tgz#9eb1c4651debcc670cc94f1a75762332bb967778" + version "0.1.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" user-home@^1.1.1: version "1.1.1" @@ -1768,9 +1764,9 @@ uuid@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" -v8flags@^2.0.11: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" +v8flags@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.0.0.tgz#4be9604488e0c4123645def705b1848d16b8e01f" dependencies: user-home "^1.1.1" @@ -1781,11 +1777,13 @@ validate-npm-package-license@^3.0.1: spdx-correct "~1.0.0" spdx-expression-parse "~1.0.0" -verror@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" dependencies: - extsprintf "1.0.2" + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" vise@2.x.x: version "2.0.2" @@ -1807,8 +1805,8 @@ which-module@^1.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" which@^1.1.1: - version "1.2.14" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: isexe "^2.0.0" @@ -1840,8 +1838,8 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" wreck@12.x.x: - version "12.2.2" - resolved "https://registry.yarnpkg.com/wreck/-/wreck-12.2.2.tgz#e21823d34c36d672004eefa347ae8c4f6050e3db" + version "12.2.3" + resolved "https://registry.yarnpkg.com/wreck/-/wreck-12.2.3.tgz#3cb2ea880ea51c5982a23fa8182c53970b9b3afe" dependencies: boom "5.x.x" hoek "4.x.x"