{"_id":"hull","_rev":"160-b9085c7877e3e6488f3ef42b3da51d1c","name":"hull","description":"A Node.js client for hull.io","dist-tags":{"latest":"0.14.2","beta":"0.13.19-beta"},"versions":{"0.1.0":{"name":"hull","version":"0.1.0","description":"A Node.js client for hull.io APIs","main":"lib/index.js","repository":{"type":"git","url":"https://github.com/hull/hull-node.git"},"author":{"name":"Xavier Cambar","email":"xavier@hull.io"},"license":"MIT","scripts":{"test":"echo \"Error: no test specified yet\" && exit 1"},"dependencies":{"superagent":"0.14.x"},"_id":"hull@0.1.0","dist":{"shasum":"97385396835b0343e08d5c0e27a4d1127328c643","tarball":"https://registry.npmjs.org/hull/-/hull-0.1.0.tgz","integrity":"sha512-G2vW0LDZJo3WdI3SWhdvHVRyjxdD90i0t9ZrBpLNAAhgu0S5jDJZON4jq7Hs2eJZj1yq6k7fsf4XimBX4XUl6w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA9Z6cOtQ3EKaLnaCpQN1iA5HLGNQAF0LJcQAZG3/B8uAiBI2XhgWKSTw3+pe+iIjJhYBfQWBepPT0Q5L0CdzCUsYA=="}]},"_npmVersion":"1.1.62","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{}},"0.3.0":{"name":"hull","version":"0.3.0","description":"A Node.js client for hull.io APIs","main":"lib/index.js","repository":{"type":"git","url":"https://github.com/hull/hull-node.git"},"author":{"name":"Xavier Cambar","email":"xavier@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*"},"dependencies":{"superagent":"0.14.x"},"devDependencies":{"mocha":"~1.10.0","sinon-chai":"~2.4.0","chai":"~1.6.1","sinon":"~1.7.2","underscore":"~1.4.4"},"bugs":{"url":"https://github.com/hull/hull-node/issues"},"_id":"hull@0.3.0","dist":{"shasum":"887c642f41950c4322ccddb9a6dffa3cf9b83e15","tarball":"https://registry.npmjs.org/hull/-/hull-0.3.0.tgz","integrity":"sha512-7e9xJIjYkfRzfxrWTfIoJK/mWUSh/0UbLodzBk//UachxSNHDAla+IDVUPn6lXOdw0rwwKKBew1aw2biBKcwZQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAJUeY3QYv/sVKwZLq3RgYivDrKM9ojxF/Y6aorJCvDSAiBPfgaLc7PnxOfDy8lxDjxRva4cWB0+20xytTaEJkhVcQ=="}]},"_from":".","_npmVersion":"1.2.23","_npmUser":{"name":"xcambar","email":"xcambar@gmail.com"},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"xcambar","email":"xcambar@gmail.com"}],"directories":{}},"0.3.1":{"name":"hull","version":"0.3.1","description":"A Node.js client for hull.io APIs","main":"lib/index.js","repository":{"type":"git","url":"https://github.com/hull/hull-node.git"},"author":{"name":"Xavier Cambar","email":"xavier@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*"},"dependencies":{"superagent":"0.14.x","underscore":"~1.4.4"},"devDependencies":{"mocha":"~1.10.0","sinon-chai":"~2.4.0","chai":"~1.6.1","sinon":"~1.7.2"},"bugs":{"url":"https://github.com/hull/hull-node/issues"},"_id":"hull@0.3.1","dist":{"shasum":"841e0958a71392c9e7d8d11a0844254636323b7f","tarball":"https://registry.npmjs.org/hull/-/hull-0.3.1.tgz","integrity":"sha512-EWoQoV1LiRSeR+dTNOpBOx8+B7phfmTiqV3bUmzgt6UIVufsZLG7ypYLKgCyzYu7XzrZPilUW5oBzf1EE/21FQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDwPFijD1d69eCYxPz6XpCRXly7e8vY6Ae7cOJ2o3tCYQIgFECEg4/6xneJ0+UY0iL4FIL2vtIMnZxqyUgYT8e4slY="}]},"_from":".","_npmVersion":"1.3.5","_npmUser":{"name":"xcambar","email":"xcambar@gmail.com"},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"xcambar","email":"xcambar@gmail.com"}],"directories":{}},"0.3.2":{"name":"hull","version":"0.3.2","description":"A Node.js client for hull.io APIs","main":"lib/index.js","repository":{"type":"git","url":"https://github.com/hull/hull-node.git"},"author":{"name":"Xavier Cambar","email":"xavier@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*"},"dependencies":{"superagent":"~0.16.0","underscore":"~1.4.4"},"devDependencies":{"mocha":"~1.10.0","sinon-chai":"~2.4.0","chai":"~1.6.1","sinon":"~1.7.2"},"bugs":{"url":"https://github.com/hull/hull-node/issues"},"_id":"hull@0.3.2","dist":{"shasum":"f2c518b54a5001bf19a02dc8278b3999d258593c","tarball":"https://registry.npmjs.org/hull/-/hull-0.3.2.tgz","integrity":"sha512-2L38KnZXHTT2i5/ux+cvsjrWBVjMC/QUyQzFzVv1RwMiVleyKZOk5tl9/GrU9GGTAdmoM3srAwO+aIjqUuJHug==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQsQHFTUA39aUzDJ4ZLH4vyOg0G+6pNvzBIssAM0teNgIhAKIwIUWlhiWfjlmj0Lbh0qPUJ0rzlemeki1j+h9Qrohp"}]},"_from":".","_npmVersion":"1.3.5","_npmUser":{"name":"xcambar","email":"xcambar@gmail.com"},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"xcambar","email":"xcambar@gmail.com"}],"directories":{}},"0.4.0":{"name":"hull","version":"0.4.0","description":"A Node.js client for hull.io APIs","main":"lib/index.js","repository":{"type":"git","url":"https://github.com/hull/hull-node.git"},"author":{"name":"Xavier Cambar","email":"xavier@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*"},"dependencies":{"superagent":"~0.16.0","underscore":"~1.4.4","jwt-simple":"~0.2.0","raw-body":"~1.1.6"},"devDependencies":{"mocha":"~1.10.0","sinon-chai":"~2.4.0","chai":"~1.6.1","sinon":"~1.7.2"},"bugs":{"url":"https://github.com/hull/hull-node/issues"},"homepage":"https://github.com/hull/hull-node","_id":"hull@0.4.0","dist":{"shasum":"7b6163f9cc1d6ab3f5c783905352f26b12402f0d","tarball":"https://registry.npmjs.org/hull/-/hull-0.4.0.tgz","integrity":"sha512-vRnTWG423HSsCR9HhERYxn91pHGKh6A19TMe1MPAhbEOLc2/sePsCNcC5fyR6x8ZdyVnBgTQ15kEzmfdxSIxvg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIELVsrw6cE2GSiHVIVMjGgtHc1zbZMd0YgDGB/s1fMMpAiBucMoq1N+8FRqbRXF0KTwap2pY2rIRqaeiADDpiEaxgA=="}]},"_from":".","_npmVersion":"1.3.24","_npmUser":{"name":"xcambar","email":"xcambar@gmail.com"},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"xcambar","email":"xcambar@gmail.com"}],"directories":{}},"0.4.1":{"name":"hull","version":"0.4.1","description":"A Node.js client for hull.io APIs","main":"lib/index.js","repository":{"type":"git","url":"https://github.com/hull/hull-node.git"},"author":{"name":"Xavier Cambar","email":"xavier@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*"},"dependencies":{"superagent":"~0.16.0","underscore":"~1.4.4","jwt-simple":"~0.2.0","raw-body":"~1.1.6"},"devDependencies":{"mocha":"~1.10.0","sinon-chai":"~2.4.0","chai":"~1.6.1","sinon":"~1.7.2"},"gitHead":"55c2387400ce81518241a24caa99e7b05d130b73","bugs":{"url":"https://github.com/hull/hull-node/issues"},"homepage":"https://github.com/hull/hull-node","_id":"hull@0.4.1","_shasum":"64d1910cb4d43faef9b1bb72f0b4b2cc37624bcd","_from":".","_npmVersion":"1.4.21","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"xcambar","email":"xcambar@gmail.com"}],"dist":{"shasum":"64d1910cb4d43faef9b1bb72f0b4b2cc37624bcd","tarball":"https://registry.npmjs.org/hull/-/hull-0.4.1.tgz","integrity":"sha512-WjPbcZPfbcr1TkskHvsOwSLnOO1HtTlyG4ovICtw+jEC8FdlW49ZW1v1eXYNiFLnLgs1Pr7aqiswIttJlVIEXg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAsSwcMFJZWthOF9/VrzxFYTz3kohlFU18xdBzFhmGBoAiAwmlgwIheDEXQa4xQKwApw3kkTWMKpGKT83zwv7x3CLQ=="}]},"directories":{}},"0.5.0":{"name":"hull","version":"0.5.0","description":"A Node.js client for hull.io APIs","main":"dist/index.js","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.3.1","lodash":"^3.10.1","raw-body":"^2.1.4","restler":"^3.4.0"},"devDependencies":{"babel":"^6.1.18","babel-cli":"^6.1.18","babel-eslint":"^4.1.5","babel-plugin-lodash":"^1.0.2","babel-preset-es2015":"^6.1.18","babel-preset-stage-0":"^6.1.18","babel-register":"^6.1.18","chai":"^3.4.1","del":"^2.1.0","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","gulp":"^3.9.0","gulp-babel":"^6.1.0","gulp-istanbul":"^0.10.2","gulp-jscs":"^3.0.2","gulp-jshint":"^1.12.0","gulp-load-plugins":"^1.1.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.0.1","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.3.4","sinon":"^1.17.2","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"b62471e6baedc3a9d42ca0ec94b477e4f3ced86e","_id":"hull@0.5.0","_shasum":"9e07b756d34e839a6cef9b20bf081d5dde25315e","_from":".","_npmVersion":"3.4.0","_nodeVersion":"4.2.2","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"9e07b756d34e839a6cef9b20bf081d5dde25315e","tarball":"https://registry.npmjs.org/hull/-/hull-0.5.0.tgz","integrity":"sha512-5DhMeFGPC7m91xqa/9Iao6BHmU0fcr1wG2LzHQzAVlbS9AuPExIjbBv/fS1KGSO8T/1YNvVDJiNm1HVVnEeGxg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPZP2fNw16NrNi/diPkJrD4T92O/aFL4qnUHLR/AlPtQIgRSEqXfBuL1wviisiEfRKLJVG4aBy8Sfq14ENJBL2bgs="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{}},"0.5.1":{"name":"hull","version":"0.5.1","description":"A Node.js client for hull.io APIs","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.3.1","lodash":"^3.10.1","raw-body":"^2.1.4","restler":"^3.4.0"},"devDependencies":{"babel":"^6.1.18","babel-cli":"^6.1.18","babel-eslint":"^4.1.5","babel-plugin-lodash":"^1.0.2","babel-preset-es2015":"^6.1.18","babel-preset-stage-0":"^6.1.18","babel-register":"^6.1.18","chai":"^3.4.1","del":"^2.1.0","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","gulp":"^3.9.0","gulp-babel":"^6.1.0","gulp-istanbul":"^0.10.2","gulp-jscs":"^3.0.2","gulp-jshint":"^1.12.0","gulp-load-plugins":"^1.1.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.0.1","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.3.4","sinon":"^1.17.2","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"9055a74e98c576917e42fb8933a837c470714336","_id":"hull@0.5.1","_shasum":"0de31ab0d741dcff02924387a79778e92499a4fb","_from":".","_npmVersion":"3.3.12","_nodeVersion":"5.3.0","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"0de31ab0d741dcff02924387a79778e92499a4fb","tarball":"https://registry.npmjs.org/hull/-/hull-0.5.1.tgz","integrity":"sha512-F6Kjy+uD4o56aDKT8nXDeWCBbeGlULMZEaUnMAcnvtNRw28cfa+jun073XS+K4eTsQbXzJgO+/AnrCPzFHk7lA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCY2FZKDzpLTHklmeZGIwR6prf0NhXlQ6UX0Dr7mowF6gIhAN5YZCTg/RXFXZ6wS7p3nb5EKA4LlVcqtMf1+eK1RQhv"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{}},"0.5.2":{"name":"hull","version":"0.5.2","description":"A Node.js client for hull.io APIs","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.3.1","lodash":"^3.10.1","raw-body":"^2.1.4","restler":"^3.4.0","sns-validator":"^0.2.0","connect":"^3.4.1"},"devDependencies":{"babel":"^6.1.18","babel-cli":"^6.1.18","babel-eslint":"^4.1.5","babel-plugin-lodash":"^1.0.2","babel-preset-es2015":"^6.1.18","babel-preset-stage-0":"^6.1.18","babel-register":"^6.1.18","chai":"^3.4.1","del":"^2.1.0","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","gulp":"^3.9.0","gulp-babel":"^6.1.0","gulp-istanbul":"^0.10.2","gulp-jscs":"^3.0.2","gulp-jshint":"^1.12.0","gulp-load-plugins":"^1.1.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.0.1","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.3.4","sinon":"^1.17.2","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"844a1f36f63c1894e49c57a8af927d0f1ff2e0ce","_id":"hull@0.5.2","_shasum":"e18bbbad5f2ee5f09e4401d2308900032022051b","_from":".","_npmVersion":"3.5.3","_nodeVersion":"5.5.0","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"e18bbbad5f2ee5f09e4401d2308900032022051b","tarball":"https://registry.npmjs.org/hull/-/hull-0.5.2.tgz","integrity":"sha512-YCusWSgzSmb4p/GjBZLZkWQeSBqXmeWxSfc/sW/YHItys31POLGAKQtUwWvM/iSjwEopQJz2z2b4IpRilKTtxg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/BC/va3jP5R72h4fi2M4KKjim3mpdRKMiOQvvWLmXMQIhAImXnXES3rapTrO8Njhjc4I+R+IH4dYtLq3Jl6+4oXZ2"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-6-west.internal.npmjs.com","tmp":"tmp/hull-0.5.2.tgz_1454530942975_0.2957839148584753"},"directories":{}},"0.5.3":{"name":"hull","version":"0.5.3","description":"A Node.js client for hull.io APIs","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.3.1","lodash":"^3.10.1","raw-body":"^2.1.4","restler":"^3.4.0","sns-validator":"^0.2.0","connect":"^3.4.1"},"devDependencies":{"babel":"^6.1.18","babel-cli":"^6.1.18","babel-eslint":"^4.1.5","babel-plugin-lodash":"^1.0.2","babel-preset-es2015":"^6.1.18","babel-preset-stage-0":"^6.1.18","babel-register":"^6.1.18","chai":"^3.4.1","del":"^2.1.0","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","gulp":"^3.9.0","gulp-babel":"^6.1.0","gulp-istanbul":"^0.10.2","gulp-jscs":"^3.0.2","gulp-jshint":"^1.12.0","gulp-load-plugins":"^1.1.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.0.1","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.3.4","sinon":"^1.17.2","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"b92b30c47d85d3e6e0381b198432e88e7be84364","_id":"hull@0.5.3","_shasum":"72fa490bdeff7774b6fe50bc33c84441e6fc0bf7","_from":".","_npmVersion":"2.14.4","_nodeVersion":"4.1.2","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"72fa490bdeff7774b6fe50bc33c84441e6fc0bf7","tarball":"https://registry.npmjs.org/hull/-/hull-0.5.3.tgz","integrity":"sha512-MGFwhsVUZCLRoF3snfSDMsfpuxM55UA6hlI1bSN6BKTSFQPY2NBv72TNXllZII3G/2HumULk0/oQP8fTLKwoQw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHfJH4RlSGJDrvLXLJJ5aRj2pk3amiGp+bJYbRjxSwZgIgVFf/IR/RGKj21EQ2NYWptLFvjS+NJrLQL6cxsRgcHGc="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-5-east.internal.npmjs.com","tmp":"tmp/hull-0.5.3.tgz_1454607423956_0.47991022816859186"},"directories":{}},"0.5.4":{"name":"hull","version":"0.5.4","description":"A Node.js client for hull.io APIs","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.3.1","lodash":"^3.10.1","raw-body":"^2.1.4","restler":"^3.4.0","sns-validator":"^0.2.0","connect":"^3.4.1"},"devDependencies":{"babel":"^6.1.18","babel-cli":"^6.1.18","babel-eslint":"^4.1.5","babel-plugin-lodash":"^1.0.2","babel-preset-es2015":"^6.1.18","babel-preset-stage-0":"^6.1.18","babel-register":"^6.1.18","chai":"^3.4.1","del":"^2.1.0","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","gulp":"^3.9.0","gulp-babel":"^6.1.0","gulp-istanbul":"^0.10.2","gulp-jscs":"^3.0.2","gulp-jshint":"^1.12.0","gulp-load-plugins":"^1.1.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.0.1","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.3.4","sinon":"^1.17.2","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"a1b974820efe9ede18247819e4762b24e7f5ad1e","_id":"hull@0.5.4","_shasum":"5e2431d89996c23d327a2e7acf825cf64ee551bb","_from":".","_npmVersion":"2.14.4","_nodeVersion":"4.1.2","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"5e2431d89996c23d327a2e7acf825cf64ee551bb","tarball":"https://registry.npmjs.org/hull/-/hull-0.5.4.tgz","integrity":"sha512-KGlTQQ4MBqvjiev9pbseTIdDGvkk76ZgmM4gwUv3QdRjefVn0y95nEh8C1BhCuvSzQBOXQv0fZbyqDC52jzozg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFooOXTinSAPcCyMjC7vnX7M+6CRX38vaSfOjfbqjYe7AiEAmKUR1VVRKAaQmV0Kbgx3hsvQQbFSkgeKAdSZeitPsgo="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-6-west.internal.npmjs.com","tmp":"tmp/hull-0.5.4.tgz_1454670360635_0.34438614430837333"},"directories":{}},"0.5.5":{"name":"hull","version":"0.5.5","description":"A Node.js client for hull.io APIs","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.3.1","lodash":"^3.10.1","raw-body":"^2.1.4","restler":"^3.4.0","sns-validator":"^0.2.0","connect":"^3.4.1"},"devDependencies":{"babel":"^6.1.18","babel-cli":"^6.1.18","babel-eslint":"^4.1.5","babel-plugin-lodash":"^1.0.2","babel-preset-es2015":"^6.1.18","babel-preset-stage-0":"^6.1.18","babel-register":"^6.1.18","chai":"^3.4.1","del":"^2.1.0","eslint":"^1.9.0","eslint-config-airbnb":"^1.0.0","eslint-plugin-react":"^3.9.0","gulp":"^3.9.0","gulp-babel":"^6.1.0","gulp-istanbul":"^0.10.2","gulp-jscs":"^3.0.2","gulp-jshint":"^1.12.0","gulp-load-plugins":"^1.1.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.0.1","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.3.4","sinon":"^1.17.2","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"ff6523ca1dd86afba325e90d8801989b235de426","_id":"hull@0.5.5","_shasum":"f65bff72a7d478fcd768bb588a037d97b8b5597e","_from":".","_npmVersion":"2.14.4","_nodeVersion":"4.1.2","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"f65bff72a7d478fcd768bb588a037d97b8b5597e","tarball":"https://registry.npmjs.org/hull/-/hull-0.5.5.tgz","integrity":"sha512-aDUQLqjuUecJN+iHuOYLldShIjqT0w1N1Wje4D13IcyV5mhyV5PmcmEDr0mN3PC5SiVY13hG2CmJk9zoz4pGxg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG4jw7m6S0+dF2WNj4YA8U4megx6Kseiz8RcGEIbpHazAiAUrePkYgs5x5ZOnLrk5sFbQBnscZgEd5FDRxVZoR3FNQ=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-5-east.internal.npmjs.com","tmp":"tmp/hull-0.5.5.tgz_1456229113722_0.5468998493161052"},"directories":{}},"0.6.0":{"name":"hull","version":"0.6.0","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.4.1","lodash":"^4.5.1","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.0","connect":"^3.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"03113113b3dde04456400ba10f22e64584ab01f5","_id":"hull@0.6.0","_shasum":"79031817ab5cdced94db4107c00b8b76e0e13b1c","_from":".","_npmVersion":"3.6.0","_nodeVersion":"5.6.0","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"79031817ab5cdced94db4107c00b8b76e0e13b1c","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.0.tgz","integrity":"sha512-mtUzUQ/xhLDhUKyRql6b4D4/Hja5jqMTyKQYnazJ1CzSZYaqb69Dvz5mohSzwW6SyMNESX/jq6rbN4e+p08egg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDtNp7LNXVmc78mpOY868wqqE9gI629Wk0VDfv9XwZAzAIhAJSqhiniCDNguoXcvO4EZIxNkGKwcy7K9BD457Ynzps7"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-9-west.internal.npmjs.com","tmp":"tmp/hull-0.6.0.tgz_1456616262527_0.8420420268084854"},"directories":{}},"0.6.1":{"name":"hull","version":"0.6.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.4.1","lodash":"^4.5.1","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.0","connect":"^3.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"625023473f73c7159e7cbecaec7638e33844f435","_id":"hull@0.6.1","_shasum":"f79c11bfeec273cf35511edcb64a3cddcb0c9c32","_from":".","_npmVersion":"3.6.0","_nodeVersion":"5.6.0","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"f79c11bfeec273cf35511edcb64a3cddcb0c9c32","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.1.tgz","integrity":"sha512-cDLdkWi7PMsKKi2v5oiJKvGOMnwLFxWFF2IGRtcJTbTX57Rlrtpoutgdi3r/5vBgAgTijeMVSKn5RDwt3eumkA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAxPy3AWTiDiBeViZWRrq5ueMs6E4ZZ7i8xlMYGfORzJAiAsyLQjVL4I1nfhTsX4XtELs6/v8SOAUfyAZxB7oixgyg=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-5-east.internal.npmjs.com","tmp":"tmp/hull-0.6.1.tgz_1456832752728_0.2729260560590774"},"directories":{}},"0.6.2":{"name":"hull","version":"0.6.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.4.1","lodash":"^4.5.1","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.0","connect":"^3.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"f52d92b5f0801dafd36eda9c4ef0be15061abcfa","_id":"hull@0.6.2","_shasum":"085d376ff427ed0cbecbae599742ae91de5f1525","_from":".","_npmVersion":"3.7.3","_nodeVersion":"5.8.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"085d376ff427ed0cbecbae599742ae91de5f1525","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.2.tgz","integrity":"sha512-8wEq4v1I9H4sefcnuXzzh4zmd3zkeRJX04UlNiYKXo1aN/9vwtnyUU5cLWtsI/XwJFsmGW8NkN2D9I9piVkoMQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFBCR9LXDL1p6eWxJXSuQWsVyZg6odIedIpUUpwY3PP4AiEA09QPsHXaO9wpndk65Dn/53dKnFYzdSCsRYp9AxRqTdk="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-13-west.internal.npmjs.com","tmp":"tmp/hull-0.6.2.tgz_1458664123253_0.5093080697115511"},"directories":{}},"0.6.3":{"name":"hull","version":"0.6.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"connect":"^3.4.1","crypto":"0.0.3","eslint-config-airbnb-es5":"^1.0.8","gulp-mocha":"^2.2.0","jwt-simple":"^0.4.1","lodash":"^4.5.1","object-mapper":"^3.0.0","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"d05408d8313d95a97beed4a8d59059fe67b0bf61","_id":"hull@0.6.3","_shasum":"51057cb7e1eede4583a6e622cc3dd788f86fabe7","_from":".","_npmVersion":"3.7.3","_nodeVersion":"5.8.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"51057cb7e1eede4583a6e622cc3dd788f86fabe7","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.3.tgz","integrity":"sha512-oKsdfuDv78BOUJdGwJdDPDE+wOMUxHi23f7i1XSIqeZ2a279MJ8Dh0Y5DpEbDYsJqicK9zEmX1IcqI6xwiFenQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwn+789yQf4VrDn/WNwbXpJUVTlrLt4w0LD5CJWGvECQIgVnk6lObm539tNikEdss4MtacYjEERpl+M5Cu/OmqRYo="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.6.3.tgz_1460630000800_0.9887347186449915"},"directories":{}},"0.6.4":{"name":"hull","version":"0.6.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"connect":"^3.4.1","jwt-simple":"^0.4.1","lodash":"^4.5.1","object-mapper":"^3.0.0","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.0"},"devDependencies":{"gulp-mocha":"^2.2.0","babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-config-airbnb-es5":"^1.0.8","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"7550728bb3a59ec052fd260e9513a5df82f220f5","_id":"hull@0.6.4","_shasum":"29dc989200cfdcf9d72169a1468b7d73f5242dec","_from":".","_npmVersion":"3.7.3","_nodeVersion":"5.8.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"29dc989200cfdcf9d72169a1468b7d73f5242dec","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.4.tgz","integrity":"sha512-Okbvat52poYv8zLolTQlonVNHFs2H3kSYhDNEPknnklZzq9b9dwI7E59YoSu4aAJQY18cNxDI1452B0c/6fpVA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHfK1cjDa8aM4qWQert6+fZ5CoZ3rJcp1V0AcDx3UjRKAiB7U3f3acA8/5m4OXQjon1xfKvLZD77qwk26MS4yq9wow=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.6.4.tgz_1460635494829_0.5298400179017335"},"directories":{}},"0.6.5":{"name":"hull","version":"0.6.5","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"connect":"^3.4.1","jwt-simple":"^0.4.1","lodash":"^4.5.1","object-mapper":"^3.0.0","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.0"},"devDependencies":{"gulp-mocha":"^2.2.0","babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-config-airbnb-es5":"^1.0.8","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"e8dd873032a170b3a7a64fd4fc031f71edc1d0e0","_id":"hull@0.6.5","_shasum":"5d563034cae744e063b71a786b926bb49ffaafa8","_from":".","_npmVersion":"3.7.3","_nodeVersion":"5.8.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"5d563034cae744e063b71a786b926bb49ffaafa8","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.5.tgz","integrity":"sha512-EttLJ4gtVIsASRrCmaF9Y+16U4fKrZq0SwO6DPrbJznrko2sgKQuMh3jcFoSz/fZjvItq1dbfLWD4BG/M6MrzQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEtIaF8234/2Q6g6ZcD+1M1Jnv5gC4pu9Ly4BW/bd1qIAiEA0wW5bBMCAWtjRYVTfpMSyKwEEhSg5eCCH/pdYqVD9D4="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.6.5.tgz_1460990690955_0.9273550687357783"},"directories":{}},"0.6.6":{"name":"hull","version":"0.6.6","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"connect":"^3.4.1","jwt-simple":"^0.4.1","lodash":"^4.5.1","object-mapper":"^3.0.0","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"gulp-mocha":"^2.2.0","babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-config-airbnb-es5":"^1.0.8","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"08061352d66b9aa92f116bcf12baefcd5d259433","_id":"hull@0.6.6","_shasum":"8ccdd54f055d010acb75b98257c6ed913b8ba642","_from":".","_npmVersion":"3.7.3","_nodeVersion":"5.8.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"8ccdd54f055d010acb75b98257c6ed913b8ba642","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.6.tgz","integrity":"sha512-19pC3yvPUhc5ruCi7NlzC0kdGWg2G9Ziaj/8DLOZKm/JOZFKyqAvroOInZKK0c+hx6Ek6Oq23a2xINaSBVhe4g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCjEbHwGY5DpsoT3cuWb06/XY3pGlDtS+wwJjQGjHLdPgIhAPTpTjm3WMyWELdMfHopR1vPZk3slGfnHDtdZQjK8lVK"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.6.6.tgz_1460992330287_0.6996919445227832"},"directories":{}},"0.6.7":{"name":"hull","version":"0.6.7","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"connect":"^3.4.1","jwt-simple":"^0.4.1","lodash":"^4.5.1","object-mapper":"^3.0.0","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"gulp-mocha":"^2.2.0","babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-config-airbnb-es5":"^1.0.8","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"024ebefaf490fc04a8e6d31ea8def252e12719e0","_id":"hull@0.6.7","_shasum":"56a63c9120be6e9192583b2eb6c162d4b81b02e2","_from":".","_npmVersion":"3.7.3","_nodeVersion":"5.8.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"56a63c9120be6e9192583b2eb6c162d4b81b02e2","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.7.tgz","integrity":"sha512-6s1x3LwasD8Nv/v6b8RHUjGN+DMzGfhc7yTvp3K5rqQuIGqTPjyCPg15EQTcQDlhaBkjCsx+3/EuZhASVNzOdg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCMt9BVlfkcBvYp/ZvGKIliBzwrwJPy54CBvUTqIfVrZgIgDpgVUNreUTccDyIebkAdsPdryTnxH7meveDyNZCejnc="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.6.7.tgz_1461253924502_0.2612997244577855"},"directories":{}},"0.6.8":{"name":"hull","version":"0.6.8","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"connect":"^3.4.1","del":"^2.2.0","gulp-babel":"^6.1.2","jwt-simple":"^0.4.1","lodash":"^4.5.1","object-mapper":"^3.0.0","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"gulp-mocha":"^2.2.0","babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-config-airbnb-es5":"^1.0.8","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"2e5964fb63d3cbe746beb64e772073fde0385344","_id":"hull@0.6.8","_shasum":"2e912415346bd493dd278e97c844e2cb05434782","_from":".","_npmVersion":"3.6.0","_nodeVersion":"5.6.0","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"2e912415346bd493dd278e97c844e2cb05434782","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.8.tgz","integrity":"sha512-XESsJlTMKWQaHgOq1fJPZ4NbYxMAAGxsb7GqhScmAbv43VYuSuKjwZ1QGIK2bAscExB7wnqi+n454aFrPenbjA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICf1VEyHBHz1WC40rZ5K71y7jNOdZopmz8WjP/B1MGd8AiA1Dp/OEqccC07PbDpra57FhXDBx6a1sa0aQBkMYJyZnA=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.6.8.tgz_1461857285596_0.7871672906912863"},"directories":{}},"0.6.9":{"name":"hull","version":"0.6.9","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"connect":"^3.4.1","del":"^2.2.0","gulp-babel":"^6.1.2","jwt-simple":"^0.4.1","lodash":"^4.5.1","object-mapper":"^3.0.0","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"gulp-mocha":"^2.2.0","babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-config-airbnb-es5":"^1.0.8","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"867c2796d237013313b5aa2833dc6c40871a405a","_id":"hull@0.6.9","_shasum":"f640a7cbe20b076b2be806345513c2976a2f60cd","_from":".","_npmVersion":"3.7.3","_nodeVersion":"5.8.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"f640a7cbe20b076b2be806345513c2976a2f60cd","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.9.tgz","integrity":"sha512-q0J3lba2uIk2aot8RVNRhiemOUy2akBtpzgnyhMLCj2A4iJ4TL7vPCSg5SP4jUFMmkFwlOzqI4Im546Ud8XGmw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF/jTx+ohN1yksOnXgSNE6r33JXZRv/D3IQjGGy+mb9LAiEAwsMX2TxofDM35W+YOivq19wvO8r/bKKqo+xFOAqMFyQ="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.6.9.tgz_1462188714847_0.961197602096945"},"directories":{}},"0.6.10":{"name":"hull","version":"0.6.10","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","prepublish":"npm run build"},"dependencies":{"connect":"^3.4.1","del":"^2.2.0","gulp-babel":"^6.1.2","jwt-simple":"^0.4.1","lodash":"^4.5.1","object-mapper":"^3.0.0","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"gulp-mocha":"^2.2.0","babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^5.0.0","babel-plugin-lodash":"^2.1.0","babel-preset-es2015":"^6.5.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.5.2","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.2.0","eslint-config-airbnb":"^6.0.2","eslint-config-airbnb-es5":"^1.0.8","eslint-plugin-react":"^4.1.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"9f3aee971311543e144e5c9d2fb1d54a4f7ed6b7","_id":"hull@0.6.10","_shasum":"bca219059e11ca8b8128588bebbee91a94408d7b","_from":".","_npmVersion":"3.7.1","_nodeVersion":"5.5.0","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"bca219059e11ca8b8128588bebbee91a94408d7b","tarball":"https://registry.npmjs.org/hull/-/hull-0.6.10.tgz","integrity":"sha512-nYWfBauvcM85QmkIqxJ9hspgk5XSmTH7cTz46y2OmUWP0Kg79iQFxKqv4jQihob23NjuRCIrwJgeKaecuM2kOg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcDfdrr+fm996mY9aQDPwji22Cg+kCN+iMZ1rvL5UmXgIhAOGrMSXVu8UH5UeTNhjIQS0SC83YOTQgpCvQnUL/BrDA"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.6.10.tgz_1462701588348_0.49133464647457004"},"directories":{}},"0.7.0":{"name":"hull","version":"0.7.0","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"body-parser":"^1.15.1","connect":"^3.4.1","del":"^2.2.0","express":"^4.14.0","gulp-babel":"^6.1.2","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^6.0.4","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","eslint":"^2.12.0","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.8.1","chai":"^3.5.0","del":"^2.2.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"e80fd3900f8598638e22f0cb957b341e4ce2b4f3","_id":"hull@0.7.0","_shasum":"5cbf867de9cf12ea4ac579103cf536fb6757fbb3","_from":".","_npmVersion":"3.10.0","_nodeVersion":"6.2.1","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"5cbf867de9cf12ea4ac579103cf536fb6757fbb3","tarball":"https://registry.npmjs.org/hull/-/hull-0.7.0.tgz","integrity":"sha512-JR8BzUPWiXvY5/EXQP5vuY0ijLx1EDm2THgpRHvPkM/nbXU/kIYEV1+uOVWcsJPrXr4l5qQQV+OSWkQFRf1laQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKVnABtMs/pGMN0JK2rl+jLbn6fKFwB6wjt37XXOSwFAIgB+d9QwNS12NbzGf0FBlKWJ1uAAJikdKtD4Bcrwoe3lE="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.7.0.tgz_1466509825125_0.10468819527886808"},"directories":{}},"0.7.1":{"name":"hull","version":"0.7.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.1","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.0","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.5","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.5.1","babel-eslint":"^6.0.4","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","eslint":"^2.12.0","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.8.1","chai":"^3.5.0","del":"^2.2.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-istanbul":"^0.10.3","gulp-jscs":"^3.0.2","gulp-jshint":"^2.0.0","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","jshint-stylish":"^2.1.0","mkdirp":"^0.5.1","mocha":"^2.4.5","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"457085bc65b2c7765ef3f30c95af32d0d93ed05e","_id":"hull@0.7.1","_shasum":"51d710af7452ce257fa38c71650217c078cde92d","_from":".","_npmVersion":"3.10.0","_nodeVersion":"6.2.1","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"51d710af7452ce257fa38c71650217c078cde92d","tarball":"https://registry.npmjs.org/hull/-/hull-0.7.1.tgz","integrity":"sha512-RTzGWxAgVP1WF2qSjcq8bOLm57DCsq4C6AtRwUNnsIF19cEHjMur+9R2v82Uw1vlOBynPiwwvbcLVXSLdKY/SQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC8KWj5ptT4tsdLxcLwQXfj8z+ceBhy2FsZmNLA7aoycwIgNhJ9JFh3EgfknCqyM0izLvwTB9kG/Y6V1LzCpSbFBAQ="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.7.1.tgz_1466516025406_0.871104195015505"},"directories":{}},"0.7.2":{"name":"hull","version":"0.7.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.13.1","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^2.5.3","sinon":"^1.17.3","sinon-chai":"^2.8.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"207901c47f0bafb6cc8d8e728736896b9f1df526","_id":"hull@0.7.2","_shasum":"6478af8a31a170ecd33dc21ed5246694dd0d4f0a","_from":".","_npmVersion":"3.10.0","_nodeVersion":"6.2.1","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"6478af8a31a170ecd33dc21ed5246694dd0d4f0a","tarball":"https://registry.npmjs.org/hull/-/hull-0.7.2.tgz","integrity":"sha512-chFfRCkOA3V9hqhhGJzS9L64EZLxvIC3carYx8XsvIpgJ0gtvgFF3hso3yd+AcXYcJYQdClqPHqqi4JvZR4mPA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHiUZzm4AQu7aeI21/8YfNeySyQVsYSbd6WryWWlyyucAiB1oU050zdfR9hs27LFfhvdNYDlYIUoMuGLcm/xLEWXoQ=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.7.2.tgz_1466526876368_0.6831267548259348"},"directories":{}},"0.7.3":{"name":"hull","version":"0.7.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.13.1","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^2.5.3","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.1.15"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"5b5577f4fdce0a04865bbfb13d7e4cfc1f46099a","_id":"hull@0.7.3","_shasum":"9fdb5bbfffb744f3accc973482708b4e0d13a25f","_from":".","_npmVersion":"3.10.0","_nodeVersion":"6.2.1","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"9fdb5bbfffb744f3accc973482708b4e0d13a25f","tarball":"https://registry.npmjs.org/hull/-/hull-0.7.3.tgz","integrity":"sha512-mZT329gg81nZBg1VX6f5Nt3B6WoHKoclcu4lD9e+yL4VtRSg3yRASRzcnNFyMmq4u4nHzgEw0n/sntdPAPkriw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHzY0bbpmHzCefaJW0W9VgJ0UrfMc00EuUcxACMcae4gAiEA64KgmjwwGz2bCduaBZzmcIfyn8ABtL9T0k1fEyDwJgY="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.7.3.tgz_1466763451821_0.06877463054843247"},"directories":{}},"0.7.4":{"name":"hull","version":"0.7.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.13.1","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^2.5.3","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.1.15"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"48d31251b4e174a39e2e885e6ba7c796da857536","_id":"hull@0.7.4","_shasum":"9813e20d05b21ef1ba907c648e1080e82150b322","_from":".","_npmVersion":"3.10.0","_nodeVersion":"6.2.1","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"9813e20d05b21ef1ba907c648e1080e82150b322","tarball":"https://registry.npmjs.org/hull/-/hull-0.7.4.tgz","integrity":"sha512-XvSg3aC4mLzyvfM484ecJaT/zdHysC3g0X0SAm/CgP1IfSMnfHV/P3bRs3+s/zzuQiewal3JUW2nnNglZzZw5Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLeNT9TZQBy9o3zU72YDjhDUi46xdob51jQ1UZ5XUaQQIhAP8QZ4ET8509Ps+HJlGwcStX6NP0bwVa0XXBeN1dD9xm"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.7.4.tgz_1466926062949_0.6840205721091479"},"directories":{}},"0.8.0":{"name":"hull","version":"0.8.0","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.13.1","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^2.5.3","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.1.15"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"4872c9142d49f5622f442837dab116022fdc0b0b","_id":"hull@0.8.0","_shasum":"77efecc5d45d07cd07242856610a197a2ab9b861","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.2.2","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"77efecc5d45d07cd07242856610a197a2ab9b861","tarball":"https://registry.npmjs.org/hull/-/hull-0.8.0.tgz","integrity":"sha512-iq5rYMeOh4vDnzphW9V4tWwiBk8IUHzhFuXaAvy4grbxL2kO8oyIh5ApVQOKz7/YR3HQhZt1588P+yi/fMhv3g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFAIdqUtmGbhVamUYTIUO7/PPCXvQek6pfrFx3XfFA2oAiAn0jgFQo01EjKgvbqygIhbeN6drf2wc03cu6aMYXxSsQ=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.8.0.tgz_1467998254303_0.24877549847587943"},"directories":{}},"0.9.0":{"name":"hull","version":"0.9.0","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.13.1","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^2.5.3","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.1.15"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"2b558213467d22480bef25fedac24b9f9ccfc100","_id":"hull@0.9.0","_shasum":"1834a93cc96fbd87c2c4f037d71990468bcb5cfb","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.2.2","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"1834a93cc96fbd87c2c4f037d71990468bcb5cfb","tarball":"https://registry.npmjs.org/hull/-/hull-0.9.0.tgz","integrity":"sha512-KnpJpQ6hHT0/gTOv9/LFnYCur8vT3d5CziTFUYXgGXSX9LYa8zx+3EEwOFIeg7Zh+nzueLtKZCsXnbMeK+0WQA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDqneRgceqSsjjUpY4fnF1nyvUfUFvC/P/Tz7Urm6gBmAiBbs8YVWL5sBEBLRGlCfpoNTYM72whqpv+TQbQxA9il0Q=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.9.0.tgz_1468841196583_0.9609725461341441"},"directories":{}},"0.9.1":{"name":"hull","version":"0.9.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.13.1","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^2.5.3","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.1.15"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"61b1ee5ce0c2f7bc567f27d5db62510ef379b447","_id":"hull@0.9.1","_shasum":"b251b26e7e06c14744354f608625e0ae272a5620","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.2.2","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"b251b26e7e06c14744354f608625e0ae272a5620","tarball":"https://registry.npmjs.org/hull/-/hull-0.9.1.tgz","integrity":"sha512-OE3Lnnd7mlyUldRTtrvPjL7alG1r+2TILsKWfvT9obhxuATBB1nOW7EhxWerPpvfyXKzGNMP+LVqoE0qJY04bg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE95ETwIXlyC2RODN/vsOOcfpRKClhzI7HFn7LI/e/5SAiEAyUvoZH9Q+w9RR/+PQVFPKwW8u2RJ/LNOUixh1z2mWtQ="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.9.1.tgz_1468843557439_0.8174826193135232"},"directories":{}},"0.9.2":{"name":"hull","version":"0.9.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.13.1","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^2.5.3","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.1.15"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"4c0abe0bb0e67ab96e861b95d2b7cfe5eba538e6","_id":"hull@0.9.2","_shasum":"fd1af1c0f86f4b1dd54df3afa2f6db7c6296eaaf","_from":".","_npmVersion":"3.8.9","_nodeVersion":"6.2.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"fd1af1c0f86f4b1dd54df3afa2f6db7c6296eaaf","tarball":"https://registry.npmjs.org/hull/-/hull-0.9.2.tgz","integrity":"sha512-sa5SH/iWuFDfiBNR9otIW8aSGV3QM5Tl2+8+KeBXPYg4LgTva8Ipm6c4RpzNK8uSvuDdmR2blOQGAtorfIWzbA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC5xdGWv20yKe8qxGE6z6X/tkFD596fshpRde+LwNswjAIhAJa/11E/n9NEOyGo+FmK0P2ViuUidGgOU7IDu+At+cKA"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.9.2.tgz_1468852981558_0.6891342455055565"},"directories":{}},"0.9.3":{"name":"hull","version":"0.9.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.13.1","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^2.5.3","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.1.15"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"e406714bfc5f8b76151681fabd33425c42139ca7","_id":"hull@0.9.3","_shasum":"043ffb1c92b861be3616eb18f07190a78db5ed06","_from":".","_npmVersion":"3.8.9","_nodeVersion":"6.2.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"043ffb1c92b861be3616eb18f07190a78db5ed06","tarball":"https://registry.npmjs.org/hull/-/hull-0.9.3.tgz","integrity":"sha512-a6JlOf7w0FcL9CkZ6CbtuLn8jNpU4gs/4uasXzGiHvbmJaFSJGQh6szpaPJgx+OHdSm4/t6/GN5vVoZhXTQkPA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEXCsfSLuXnDn6c4eFK/MYYf3Mr+HL3YvPkVwQEh/lRyAiAJhpjtlDSSfpcrHwYhtagXInFVTBw4ITI2c9fQLOsZ2A=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.9.3.tgz_1468862602460_0.3174185019452125"},"directories":{}},"0.9.4":{"name":"hull","version":"0.9.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"`npm bin`/mocha spec/*","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^2.13.1","eslint-config-airbnb-base":"^3.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^2.2.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^2.5.3","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.1.15"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"2097d0c658e55056f39cf1e06756eecb3efe49c9","_id":"hull@0.9.4","_shasum":"9967d13a7f585052ba793d5b60c17494448b331e","_from":".","_npmVersion":"3.10.5","_nodeVersion":"6.2.2","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"9967d13a7f585052ba793d5b60c17494448b331e","tarball":"https://registry.npmjs.org/hull/-/hull-0.9.4.tgz","integrity":"sha512-Pvis/ku/YdaY5vGHn5LLAzN7CsBbAbWXuxutGV1+WhsGXojrstVQCYORNX5sUne82M4OBTVjbr65Z2zg5YW7fg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFcjQiTmT8IsmQLMV/mNdV0iK77cDWQa8iD8/dQsGNCpAiB+cA00qSEheYvbdswzdIyNwC5EjTZSjD5AnJfUrKXeSA=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.9.4.tgz_1469787774417_0.38588026026263833"},"directories":{}},"0.9.5":{"name":"hull","version":"0.9.5","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"a43dbab31278e498f3623cd4e6f6d0d0e98ea8c2","_id":"hull@0.9.5","_shasum":"3863545cea97c739700ac3847b72a907834d8087","_from":".","_npmVersion":"3.10.5","_nodeVersion":"6.2.2","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"3863545cea97c739700ac3847b72a907834d8087","tarball":"https://registry.npmjs.org/hull/-/hull-0.9.5.tgz","integrity":"sha512-3ct+WtNuo0GdkyewfmWCc7mzhBFV3t08Ux+8sPIS8bg0t2PXjNuu3jbkLJQL7QWkqM2MdfKhYvnQpz/WN9iJ+g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB7BSNA6KwEcgq+AUUBHOqONN80AgHKytDXkLQrvyY7YAiBkrEbtD4zHQ7oziknGv6MU2gCWSHhDpsw9h2nqJOB0pg=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.9.5.tgz_1470144307956_0.7962117665447295"},"directories":{}},"0.9.6":{"name":"hull","version":"0.9.6","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"1ccf1ee74591726201e1630ec711c8cda642c810","_id":"hull@0.9.6","_shasum":"42dfd5e65b55ead22fc22a1e6d6f91baf419e6d9","_from":".","_npmVersion":"3.8.9","_nodeVersion":"6.2.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"42dfd5e65b55ead22fc22a1e6d6f91baf419e6d9","tarball":"https://registry.npmjs.org/hull/-/hull-0.9.6.tgz","integrity":"sha512-VfL1sEwSTF26XCYvaSypz/BXLH5jjDsJRLHdzDGYDhxYOVrNY0mxKXRUP/tmphaHXDE7/hivpkR0S5lMj5ofvQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCr/l/VHZBgFB6StB1MRh1mfqoObrtfNImvKSru6NBTtAIhAMkHODKcnNcyk89h4rjjxEAH/CaeCPHvsrha+HCnl1DU"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.9.6.tgz_1470314396949_0.0388922612182796"},"directories":{}},"0.9.7":{"name":"hull","version":"0.9.7","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"94ef92e33294504e528dd039a8d1836b3337eb15","_id":"hull@0.9.7","_shasum":"f9eb782ebb1584dfb6f25bd2a66c8f74731d9c10","_from":".","_npmVersion":"3.8.9","_nodeVersion":"6.2.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"f9eb782ebb1584dfb6f25bd2a66c8f74731d9c10","tarball":"https://registry.npmjs.org/hull/-/hull-0.9.7.tgz","integrity":"sha512-VBp93W/OG2bvHxzbI8+KcESIg/xHXyBekFPldUdMRwJfM9NLoHE9gZGCrshsxfN32d/K2Bqp5HGx3PB9NtmoSg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGx38F+tBnih/AxprPzq2dxajuNAcl/Q5kji+lkfIQNuAiAodJwVRTsYEwqtlkPYKD1WqB91fPO1fpaFXGe4QoIYFQ=="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/hull-0.9.7.tgz_1474568029945_0.5531589600723237"},"directories":{}},"0.10.0":{"name":"hull","version":"0.10.0","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"f61071225215233a40ab0aa44bea87744341a226","_id":"hull@0.10.0","_shasum":"1164b95696ebdbccac4b3cd3ae2b65aa003587b7","_from":".","_npmVersion":"3.8.9","_nodeVersion":"6.2.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"1164b95696ebdbccac4b3cd3ae2b65aa003587b7","tarball":"https://registry.npmjs.org/hull/-/hull-0.10.0.tgz","integrity":"sha512-YDXZ2BtSovxZuTldVfTcjiqjAbSBeegijGq1G0aU9LkMENP+h6aVbIhONScCLVO9It6XsA9IYoH6DYOAsfhb0A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxRhPPybVH0C3Mm1hGiI26FvkI5RYx497XKFzG14WpUgIgF2vTpZO8mUipkOe1XfWMevfnbnakghI8Uql7rJ784d4="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.10.0.tgz_1475064721509_0.9944787828717381"},"directories":{}},"0.10.1":{"name":"hull","version":"0.10.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"50e5c8d4040384d4af75acebda50d97f1ac5fac0","_id":"hull@0.10.1","_shasum":"2537a22715dc31ba23f5470ed3f9bb7cb0e07de5","_from":".","_npmVersion":"3.8.9","_nodeVersion":"6.2.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"2537a22715dc31ba23f5470ed3f9bb7cb0e07de5","tarball":"https://registry.npmjs.org/hull/-/hull-0.10.1.tgz","integrity":"sha512-N/tPjbRu6VvThGqV03ZpjGVxxZnLSQIu++zCVj6LyPk/ZvClykAGPPgrwvNZ7KSS+KOEZuraN8fzuO8xSr9CrQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD9H07jPqsfNuZNWzuLtf2gs+j7DZ6rjwkMeZSQXHTlKQIgYoB86Ew3wyJCc/2MkFPiFMLlcsZX+LW6PAxCG+xn8TY="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.10.1.tgz_1475228882979_0.9144041321706027"},"directories":{}},"0.10.2":{"name":"hull","version":"0.10.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"41ee014013e3f26970de896db703cc1e8c446a92","_id":"hull@0.10.2","_shasum":"6d22fc848913fa88a945c556e84292f3494c4ba7","_from":".","_npmVersion":"3.8.9","_nodeVersion":"6.2.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"6d22fc848913fa88a945c556e84292f3494c4ba7","tarball":"https://registry.npmjs.org/hull/-/hull-0.10.2.tgz","integrity":"sha512-C9oDGR0pGxXvSHgYEVJ/knOwQk73NBtEGFFBCXGcCGij30MdsvLf2YDjrUQd4ch7ENYo+oBR4ReWOc5mrMYNdw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH5noWOijoOlNAPL2IiC6RkRJcBQAP0MXcDYx+RSuPimAiEA6yQpgMd0Thoc3xd7Gs/M8nuyPafvUISq4l83uhq4Q+c="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.10.2.tgz_1476189763498_0.005939235212281346"},"directories":{}},"0.10.3":{"name":"hull","version":"0.10.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"5d1b29ebb1e2b034d9a5f39199c7d5c69204adab","_id":"hull@0.10.3","_shasum":"9ed72e0f56bcf489008ac48b098faf19d24e8617","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.5.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"9ed72e0f56bcf489008ac48b098faf19d24e8617","tarball":"https://registry.npmjs.org/hull/-/hull-0.10.3.tgz","integrity":"sha512-NJa+Cv2nGrKihMBCZNLc8O7MoRCU4xeouen5i9cGH5k+8MqtIqs7CbZ1Z0H3WIK6fo7o1/kScd7VNSAgz+HPQQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEtTwulbJ9o9oSrMHak82Seep+pZnodaVJqf83CvQn1nAiEAomZrfxKkcbMY0PI5YXjKHR9DF7JPKQZL0maszXi5HdQ="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/hull-0.10.3.tgz_1478120646573_0.03000930161215365"},"directories":{}},"0.10.4":{"name":"hull","version":"0.10.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"886bd0291d1ff60abde8e892c162139bbfda203e","_id":"hull@0.10.4","_shasum":"21eb73953edece1b41523dcc2c9d2903d6f3dc30","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.5.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"21eb73953edece1b41523dcc2c9d2903d6f3dc30","tarball":"https://registry.npmjs.org/hull/-/hull-0.10.4.tgz","integrity":"sha512-bFrV4T2glLut9MY/5PX/iR9U19/MDT9lH2c7qtswsHxFIvOcdsz1osh8mLDadtRMPWa2gOt5md++6Wx0fuSu+w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDhAtKlRipfAXkC6NIWgvPo0NEmjUkK1BAqley70SftAAIgZc7tcyNlcCaLI0anL4ZliibBZVTXiSuTQeHr2cLJLOg="}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/hull-0.10.4.tgz_1478796997061_0.31864915997721255"},"directories":{}},"0.10.5":{"name":"hull","version":"0.10.5","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"52203003378ddbc870dc2d6316c7670c1c9f9274","_id":"hull@0.10.5","_shasum":"b2765b7a11094d9e9c74bfbd78505e254314d5de","_from":".","_npmVersion":"3.10.8","_nodeVersion":"6.9.1","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"b2765b7a11094d9e9c74bfbd78505e254314d5de","tarball":"https://registry.npmjs.org/hull/-/hull-0.10.5.tgz","integrity":"sha512-qQhriXvBfDF18feMOX4daXTsE20zKlRUgEv8LX5sr7n1tug790FN7tkMmkn+09MhG30XEpojcaEVeZ9jhZ7naQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC7+ajpq7Ty1W2ietL4/PpB296HIM2MA1bKhhhUyrCSYgIhAIphzABRlcbc+llgvLF6riAVPExR6MMrtsTT8iSUs/CF"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/hull-0.10.5.tgz_1481793261520_0.09640969126485288"},"directories":{}},"0.10.6":{"name":"hull","version":"0.10.6","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","ejs":"^2.4.2","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","object-mapper":"^3.0.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^6.0.5","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^5.0.1","eslint-plugin-import":"^1.9.2","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-notify":"^2.2.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"0167db4977ec079111b7f839febf277c0c23c1ad","_id":"hull@0.10.6","_shasum":"53df7a5442cfac0136d4ac796c01ce9be84f175f","_from":".","_npmVersion":"3.10.10","_nodeVersion":"7.3.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"53df7a5442cfac0136d4ac796c01ce9be84f175f","tarball":"https://registry.npmjs.org/hull/-/hull-0.10.6.tgz","integrity":"sha512-gnjRgtoZHNU0MGYgLLogERYoDisNNyW6vOgCb1NqzUVKjT9NFjvXxHyX2RDxQ6O9L2+9CXbOI6OQS4o/bDig+A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDNyzM+qDYLe4TV8TJMYDvJHDFQ+XAf4be8w0DfbvZRegIhAOebq0OrKKmiCkH/T8JSc0/Fp0rs3nfZE2JwjHAluGrI"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/hull-0.10.6.tgz_1484918787182_0.8697105494793504"},"directories":{}},"0.10.7":{"name":"hull","version":"0.10.7","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"95547ce6c8b39abdf19475af73d3f4dc7e5268f3","_id":"hull@0.10.7","_shasum":"c1f8e33e3662fd9ce86d50b16fefb51a26130a5f","_from":".","_npmVersion":"4.0.3","_nodeVersion":"7.2.1","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"c1f8e33e3662fd9ce86d50b16fefb51a26130a5f","tarball":"https://registry.npmjs.org/hull/-/hull-0.10.7.tgz","integrity":"sha512-5QqnlwUL0mKLCfORMTkhp4cyzpDhszBFFuMnbUNRiJ2+LJ5pNhmHBxN5fTjlOTlizjKB3S0tgCNwHFNqPjc5SA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFflxN31GTRpT58VYkQQ16bYfpu0kwDc2qfP9ZEmcLdAIhAOWMMqPymBWkQa3Lv3gyGUhFZExL3Sz6wd8XmRYAV+3e"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.10.7.tgz_1487335713190_0.6037046404089779"},"directories":{}},"0.10.8":{"name":"hull","version":"0.10.8","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint server","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test ./node_modules/.bin/mocha -R spec ./tests/index","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","csv-stream":"^0.1.3","del":"^2.2.1","express":"^4.14.0","jwt-simple":"^0.5.0","lodash":"^4.13.1","passport":"^0.3.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"c01dc451bfd56d4ea8850a8bb2b54a9103a4ceb0","_id":"hull@0.10.8","_shasum":"1b6eab52fb210e17945b2b6f638b9ece07a21b3f","_from":".","_npmVersion":"4.0.3","_nodeVersion":"7.2.1","_npmUser":{"name":"rdardour","email":"romain@hull.io"},"dist":{"shasum":"1b6eab52fb210e17945b2b6f638b9ece07a21b3f","tarball":"https://registry.npmjs.org/hull/-/hull-0.10.8.tgz","integrity":"sha512-D/k3dnD9FvaxnQzBiuvnn2h8qK//3G60PqsZqMl52w6btRivyW+mjvQK3j1F/PoYinjvsFyis9N0VomujvRslA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC1IXqNq9+NUou1EI9PBmb979vQeQtr4z+u39znuH72LAIhAM56NkBGZSLptgar/ww5GnPzQC3G5sLbUcxER0bFID2Q"}]},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.10.8.tgz_1488028234720_0.423438896657899"},"directories":{}},"0.11.0-beta.1":{"name":"hull","version":"0.11.0-beta.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/*","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.3.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.37.2","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","supply":"0.0.4","urijs":"^1.18.7","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.23.0","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","rimraf":"^2.6.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"455a79d5b36e3cdadc79dd84ebbf1b0f48401d49","_id":"hull@0.11.0-beta.1","_shasum":"7188de2c66f25b7cdf0f822302561fff5d75a720","_from":".","_npmVersion":"4.0.5","_nodeVersion":"7.4.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"7188de2c66f25b7cdf0f822302561fff5d75a720","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.0-beta.1.tgz","integrity":"sha512-uAgnQKMbkJOOPPUKz3SRcUXlPh4RiIzv3hz+XynJ987Es/UZCS9injhITszZKsBRh1V20Y1LPfgGWVO+ppUuDA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB0MnDQxiRzate/9BuBBCP+VHfBrDodLdq6imt+8WoqXAiAM7cjC13qaxFi28mcXLvvhaHgshMWnqQJX0NrNNzwxOg=="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/hull-0.11.0-beta.1.tgz_1488291720644_0.13665520050562918"},"directories":{}},"0.11.0-beta.2":{"name":"hull","version":"0.11.0-beta.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/*","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.3.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.37.2","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","supply":"0.0.4","urijs":"^1.18.7","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.23.0","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"29fad2d63888cb8ab016acde6be7014b47d3e34c","_id":"hull@0.11.0-beta.2","_shasum":"768afd48cffd18e20353f61a2e3b796be530fe4d","_from":".","_npmVersion":"4.1.2","_nodeVersion":"7.7.1","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"768afd48cffd18e20353f61a2e3b796be530fe4d","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.0-beta.2.tgz","integrity":"sha512-KXXRzvD+QcMPqE7DjDeqE5M0n1Ryxi44Gz/1yQqxwQDHsmH6rNj1DH2nvpgsPk+S5Jidi7cHwUHfWIVFpMrGmA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDrYKKW+43FvIpK8jLDD5LKNQLMT2uVRlPObsEQx2x9qgIgQZDgbUMT/z3TlafOqQscAOOGFxwWFBF3ALDQJ+isgQ8="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/hull-0.11.0-beta.2.tgz_1490014550195_0.10643938812427223"},"directories":{}},"0.11.0-beta.3":{"name":"hull","version":"0.11.0-beta.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/*","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.3.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.37.2","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","supply":"0.0.4","urijs":"^1.18.7","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.23.0","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"6175abbb9506c41a4046437f999939ad10747f8b","_id":"hull@0.11.0-beta.3","_shasum":"a91a2e456f8be6acfba0ea73801f89dd76777151","_from":".","_npmVersion":"4.1.2","_nodeVersion":"7.7.1","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"a91a2e456f8be6acfba0ea73801f89dd76777151","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.0-beta.3.tgz","integrity":"sha512-Bjiurp9dUq3RBgRdKSpD2xz22ehj0yK5wqD83aHJYTfAbmTaUzbdIFxQeHGSIdj+73dMZ+2z2C27+nC7qqw8HA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUWK+LdJD59xelCGeSFqcTOlcaog4E1CY9+V6rPVlyyQIgG0FS1iKxZSbXUhCKqT2R4/4CHjDOnIySgDwGx/wNfvU="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/hull-0.11.0-beta.3.tgz_1492513405992_0.869623324368149"},"directories":{}},"0.11.0-beta.4":{"name":"hull","version":"0.11.0-beta.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/*","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.3.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.37.2","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.2.1","supply":"0.0.4","urijs":"^1.18.7","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.23.0","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^3.0.0","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^1.17.3","sinon-chai":"^2.8.0","updtr":"^0.2.1"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"99ac14fcc6252e20f566d3d990db79fa8acbb437","_id":"hull@0.11.0-beta.4","_shasum":"297b3d20c504ba2447b99f6b3952bc62da2b3dd8","_from":".","_npmVersion":"4.2.0","_nodeVersion":"7.9.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"297b3d20c504ba2447b99f6b3952bc62da2b3dd8","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.0-beta.4.tgz","integrity":"sha512-a6QLYGusYWt12HJ97E2kDMraBfV8N9zPfeUlvBGhSEj17IQRtjctWQkx6GFiJdT/ZFTcbqGCTDNbaJPvFtqYjA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFfQR4EihuDqux26lKzgb4rVeKljpTBF9sPQNU/eHC4zAiBbVeZpV8ULd2xFPs0xkg6DXkL6MN9+JQ4I44LHwlxAxw=="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/hull-0.11.0-beta.4.tgz_1493307362107_0.6576547354925424"},"directories":{}},"0.11.0":{"name":"hull","version":"0.11.0","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"3e03f63155966c517f1502d70fac5bf6469d2993","_id":"hull@0.11.0","_shasum":"5797c7acdbd7e9b4920fd8acf5430449f91f0d10","_from":".","_npmVersion":"4.2.0","_nodeVersion":"7.9.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"5797c7acdbd7e9b4920fd8acf5430449f91f0d10","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.0.tgz","integrity":"sha512-koTGKfJxN+AmnsHrNGspoi6L5nDjDqf72GbRlhAfTZNyPD9kI4QO7QVIIEpiga9DaX28n6L/YEnw1i/j3hTucA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB+nUoRnu+AuoTgrRvcc7E8M89tuYuVsN4Ru1Df5304IAiEAtlyFhHyhc1h3Jv6jCUJwsrHLyTo4+89tW80J9oVUN7o="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"packages-18-east.internal.npmjs.com","tmp":"tmp/hull-0.11.0.tgz_1493901568321_0.36243406240828335"},"directories":{}},"0.11.1":{"name":"hull","version":"0.11.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"e8def99de06e7b297d353fe3ca922801643cd4cb","_id":"hull@0.11.1","_shasum":"3093a16d750eb3212ad7b7e618b4ef08528a4b09","_from":".","_npmVersion":"4.1.2","_nodeVersion":"7.7.1","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"3093a16d750eb3212ad7b7e618b4ef08528a4b09","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.1.tgz","integrity":"sha512-cZw0z571DscebXYuM19hEgB1fzaejn/7aR53pA4CoszQnIdfRJ7QtX7hpy+foUC4FbMkrA/YzNw0LkTXv3Rqvw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHdLjssqYiqJAoWi0SRAC2nkn4hLCsyNw1xGcrd7cIySAiBwyxoi8Gwo5ZRK9DM7MkbHmONFcJMdGEdwyeL+tBGhYA=="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.1.tgz_1495705949943_0.11348974728025496"},"directories":{}},"0.11.2":{"name":"hull","version":"0.11.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"1e2c6e56b67ff74c2d1c9297742ab935dca4579d","_id":"hull@0.11.2","_shasum":"0ec62fbc5cbb635ce7d40cefee22c34969b50c5a","_from":".","_npmVersion":"4.2.0","_nodeVersion":"7.9.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"0ec62fbc5cbb635ce7d40cefee22c34969b50c5a","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.2.tgz","integrity":"sha512-41rGogjLw91xkcRtiSxYDpeMGallEtkkoQXNOwisUneYTiRI+Oo9DuG7/7h4ftEIlBArDuy4NBEMsgNldOHOCA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCp+jfysDsLJKywChYG79X+yH9dpKOX8P58OgekI5NxsgIhAPzBYYaoZ/qo6lXG47hqdIk8u+yd4MrKhfoNGjRvt4LG"}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.2.tgz_1496070075765_0.7511648468207568"},"directories":{}},"0.11.3":{"name":"hull","version":"0.11.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"3527485183c370db889a37ee2d59020c421b20ec","_id":"hull@0.11.3","_shasum":"f2157fbf7d95e6febb754cbda5e4cca772766d5c","_from":".","_npmVersion":"3.10.10","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"f2157fbf7d95e6febb754cbda5e4cca772766d5c","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.3.tgz","integrity":"sha512-5ZlDkjl91Hk+vcQGqlwlhRWQkm7RadjKJ+VZfsBZm8wC/E9A/bMW9aCbnP+YKg4u+lDGn6AyMuEJ63HH+bwCbw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGbl9p1mZ5PIVIE53fsAjyyA0xWaWnLJTfpBFMgB3/ngAiBEuRvE7nJN39pnuno+m6hj4SduMyMSF03wR8Zi4Rwx2A=="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.3.tgz_1496936418047_0.25341320829465985"},"directories":{}},"0.11.4":{"name":"hull","version":"0.11.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"3bb6a18812aaf2f4f9ced5caf6708a0047795500","_id":"hull@0.11.4","_shasum":"ff9a91130bdd75a1b03f63f382d27f268921f076","_from":".","_npmVersion":"3.10.10","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"ff9a91130bdd75a1b03f63f382d27f268921f076","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.4.tgz","integrity":"sha512-FtH0WHF9xBn5AFCfCYH3qKTRWHSKB28JUiyRqMHCoArUhzAJTDEaZnnIL++OxdEq+ADmE3oCQXPTxLo6A9rO2w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwq02T7WsLV1vpx0e4y1lPeuCnNBq35egtvf5dhEM07gIgfIqoFMUenE0QTYc9eGraKguasjfV/nSa5gYK/37Ke/Y="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.4.tgz_1497881989463_0.4496208697091788"},"directories":{}},"0.11.5":{"name":"hull","version":"0.11.5","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"a3a03575521d3477b0c3ba56f52f51e4aff9ee5a","_id":"hull@0.11.5","_shasum":"cd43316f2bf36d922fb9459a11e71b2c64c47d07","_from":".","_npmVersion":"3.10.10","_nodeVersion":"8.0.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"cd43316f2bf36d922fb9459a11e71b2c64c47d07","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.5.tgz","integrity":"sha512-54fRkG+l1eXMxuSs2ZWM8u9+bjyWxNLRtdRWYY85RKeN9YaOSCd8RgFtBqiirNV1zloD+60NlsPW5pqobt/ukA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICiAkMA+475d3GVkaLib57WCKXGtnHTnT3vy1zOJU1kVAiBkwrt/wLr9jpTDvr8KL6g37MCoJaBEF0iEQIOmagBWxg=="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.5.tgz_1498145717368_0.17726843198761344"},"directories":{}},"0.11.6":{"name":"hull","version":"0.11.6","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"ef944b10cc58f2ef6eb3aa99861ed5b76e5b83d4","_id":"hull@0.11.6","_shasum":"77407bce060e901f3a0ac67e68b9b6b121fffa16","_from":".","_npmVersion":"3.10.10","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"77407bce060e901f3a0ac67e68b9b6b121fffa16","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.6.tgz","integrity":"sha512-DJsaihfNiGrwYqCTZtRJ/D5NJdQZ5vxjlmtxLr8zRAhsQPLO2uD2UErjjU2ERzEien559++yO5vnYl0JmOgang==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/GPH+C3Rl74l/jSbxqgXdLVF4JA7IK8nJUA5oIHfwaAIhAOYmKFmYVpwpL5OgmVJjmBEwz5+EVqu1ORKAjLm0mqaQ"}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.6.tgz_1498568270931_0.14507077308371663"},"directories":{}},"0.11.7":{"name":"hull","version":"0.11.7","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"03177882a919e87dbdd502191b8d8074a079e8df","_id":"hull@0.11.7","_shasum":"5583fc5f981cc1ad11dae9e499067d491867e1b4","_from":".","_npmVersion":"3.10.10","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"5583fc5f981cc1ad11dae9e499067d491867e1b4","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.7.tgz","integrity":"sha512-z4YhJ5jxyZywnOOUFNvVwths/OxKP9j0V5RPKalNxAHWG/QOrRr0b9y+Ri4yXGLMgqjN5OOhqA9JY5Hv0DuA4w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAd4hodpYfqeYmgRaymXQCyyi/J5ap51caRuW+3BkrWTAiEA+lcN/hCH5h4aIkSuqre4okPerT5bKcC9g+sLD2jyzYA="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.7.tgz_1498819838629_0.2254570231307298"},"directories":{}},"0.11.8":{"name":"hull","version":"0.11.8","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"a14c612c9f37eab5f91014fa4b7661d11a8156e2","_id":"hull@0.11.8","_shasum":"a2200488ca05985507df0f655b097791b5851376","_from":".","_npmVersion":"3.10.10","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"a2200488ca05985507df0f655b097791b5851376","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.8.tgz","integrity":"sha512-qAkFKVYdBH7WZReK2KXK2HZJfE+PBC8xuPzEDiyD63OMjb0Kxsr9NyEebF74VWO9DhfB472jJQBQouDQYLPk8g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHk4JDrjPjrizd3I3CLfFG+jwmEtKIiEglx+50by/ZikAiBvgQed8Su0lgU9aadCRP3+PJHezvfd3TEHFqnR9QCoDw=="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.8.tgz_1499250819328_0.8048277078196406"},"directories":{}},"0.11.9-beta.1":{"name":"hull","version":"0.11.9-beta.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/* ./tests/**/* ./spec/*","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"94212fec6f73b1f756ba0c98b439026abd239af1","_id":"hull@0.11.9-beta.1","_shasum":"a04139ba4e376b5bd0a65accbcde5cf62ca3635a","_from":".","_npmVersion":"3.10.10","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"shasum":"a04139ba4e376b5bd0a65accbcde5cf62ca3635a","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.9-beta.1.tgz","integrity":"sha512-iLtN5nZj2S5YXVpAyBApWw3tlw+nMEFmY/THcZ+66I3Xg5G+fG9P4NlhBW0BmBMk/SVTlzplKJLLQjsbifN/hw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEl/lkeOhgo0RynOCUao5H5UrnYr9djfaIWjuqNOZKJsAiAr3Ewr4iAut2HnII535wotj6/PPwAHBl4uJqYOR5keZA=="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.9-beta.1.tgz_1499363357796_0.3758975029923022"},"directories":{}},"0.11.9":{"name":"hull","version":"0.11.9","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"7fdd700c85034d2e62b35bb2c2937f6612c41ea9","_id":"hull@0.11.9","_shasum":"ba0be5e149fea12845bb30ff2079389527e6d5b1","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.5.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"ba0be5e149fea12845bb30ff2079389527e6d5b1","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.9.tgz","integrity":"sha512-8Gw0mnG40NY9WN4lkACAcARSes0pmgX46pXokvjZrKSoO1J7W19FznNeXn1gvtkSGmSRoP2dbWMsgN4uOnM6kg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEy0HfeoE8ZidP0V8/FE+shAogLkmiPG9Xy9KuOIKJPrAiEA82JukeEgglqBzz6YtM9HzVtj1y/6tTGi4SynCFne26I="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.9.tgz_1499806415451_0.35763744660653174"},"directories":{}},"0.11.10":{"name":"hull","version":"0.11.10","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","JSONStream":"^1.1.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"8180baeb66945f090666bceab7a960fdf1b1b5a9","_id":"hull@0.11.10","_shasum":"c22ddc4f4d60d091245315dcdb375a1dc0a6fd2c","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.5.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"c22ddc4f4d60d091245315dcdb375a1dc0a6fd2c","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.10.tgz","integrity":"sha512-IZAUzR5lPhWsEUov7iAcGs14aR2aq3prT6LUUjpRJeodDLTpIpM1hJYZ1VXVdRXFCQ1MtKZ/e9roTlNvs85cQw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDhqgvvKdaYc09PAv4fz/5BmsA5tQmvlKgSLpEYbV4mMAiEAkTpqo+XQuxZ91jnzo+d18dNkApbvD4zREtBPRQ7qlW4="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.10.tgz_1499807547468_0.15717711439356208"},"directories":{}},"0.11.11":{"name":"hull","version":"0.11.11","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","JSONStream":"^1.1.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"0ad67bffae8ad61c7dfed786cdbd4ab625ffb324","_id":"hull@0.11.11","_shasum":"c684e70ca18c53093e77729d45f75b5d41207d12","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.5.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"c684e70ca18c53093e77729d45f75b5d41207d12","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.11.tgz","integrity":"sha512-Jdkx0E3qbICn/MkcuHW+OVz2dJ/CLVf2Cl+42eZ83WiDE7tHZug8thSjNqVW1J5OJFwmesqOYY1mKfUpb4+ORA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICJsbfGyeXslvNMo6zyxhCaaJOr8aRjuWByvJL5FdLplAiEA/IXtj9fzLQIneQ5DYJJnYap3vjK7Zg2sWSIuRZVVQQs="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.11.tgz_1499809084233_0.38629506924189627"},"directories":{}},"0.12.0-beta.1":{"name":"hull","version":"0.12.0-beta.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"^1.1.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"ca1641fab18987046440e009b0bbebd932777b17","_id":"hull@0.12.0-beta.1","_npmVersion":"5.0.3","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-EgYrngB/GI5rIjZUKFFfQv9FBbcbkCcMnz+ucc6xDV5aqjOWe0sMJRdyDY2ZIYi1xrxJzeSjbAjycxawIQLg4Q==","shasum":"ef96f524a07a479001bd156c406f5b415db8d522","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.0-beta.1.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEuAJqLDmt98Cs+VspnBlXW1DZAM2din1wJlZ+Jx8Q0SAiAc9cD7jBLnlPvHUTP6/pkEa6hsCxD8ye4DyiAwFjfUoQ=="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.0-beta.1.tgz_1500373994757_0.6276349041145295"},"directories":{}},"0.12.0-beta.2":{"name":"hull","version":"0.12.0-beta.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"^1.1.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"4793c1d6796e5fd9643155c138bee29ae1997c8c","_id":"hull@0.12.0-beta.2","_npmVersion":"5.0.3","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-YeXxJHWAIEJJhuU2YMgtI4mQGnAJHtGEKThhQrfvP7bI7bY68pSITt7qYx22MxBQByOk6yhLFdnXWAIRJwQxxQ==","shasum":"2a89a6785356977bad58923c7ece391d2aaf2164","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.0-beta.2.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICj+mrEHvHAKBBn5UD0W+7TiFfX1kHyyfvYgsA36vOKkAiEA+p59U0QMzLhqeEuhABjGE3fuGxHYP1zQboiH2hXrB5s="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.0-beta.2.tgz_1500376474171_0.8200105363503098"},"directories":{}},"0.12.0-beta.3":{"name":"hull","version":"0.12.0-beta.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"^1.1.0","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"bec9b47c3f580790666752ac103a7f7aec627a69","_id":"hull@0.12.0-beta.3","_npmVersion":"5.0.3","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-UMsJ4cgj6U577paMlZAIc/3J7d0Dw3r6BLvQF5MaRy1ubEOakj9l0pp3felgEdD4Cfrpy/IdobJapRrkhzUm9g==","shasum":"690fb6036b1aeb9ef1ac2c21daff4ecbc865e736","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.0-beta.3.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDatB7UCbvz5ATIRDy5jbKZVejJLoXMgpIlhfD4ejdnBwIgCRnLShntBUF/2lL4/RC0Ue70/lYfo4Vel4YrNJS1kNQ="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.0-beta.3.tgz_1500388176687_0.18437525257468224"},"directories":{}},"0.11.12":{"name":"hull","version":"0.11.12","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","JSONStream":"^1.1.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"6ab6d684c02d4b675956f548d0efc4bc5d8e71c9","_id":"hull@0.11.12","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-TayinjSVr5YaDZisOXWJiZIhMuK+8FLEwpUjh+DdsN114WPp7e4YOtXsHfeXEDwONTX3MBN17IYHURWiffA4Jw==","shasum":"614781d00cf36f3cea906812c63b8cc87d6375fc","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.12.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHHas11SvyRaNnnzVRmUnvDB7q24RsxfdqVIfCd1ljWYAiEAqCHQsB9wDYj6Abs/fsEnMca2u895H9pRIfZN4GkGwME="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.12.tgz_1500627465626_0.7877025231719017"},"directories":{}},"0.12.0":{"name":"hull","version":"0.12.0","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"bf30228a775ee018cd7b0b2fd85b717947ab7f68","_id":"hull@0.12.0","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-zbwDYUqtqyss964PpY2xSMAnAcVBtbQSkOKx+dL3Gj8Xn8vR2mnQ3txE/b44Pr7+5mL0XP7l5Arxile4+5NEnQ==","shasum":"6312b3e9905c6aa6ea527261e9469c26c694801a","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.0.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICEuA7riiD/nesjC9LoVOUoNw+gj75D48kbODtOgoYR5AiBZF2iAjQgtxXdmRRTy4J0OM5QR2gqmBjsWkU1qEw26/g=="}]},"maintainers":[{"name":"michaloo","email":"michal@raczka.me"},{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.0.tgz_1500637327093_0.2486078303772956"},"directories":{}},"0.12.1":{"name":"hull","version":"0.12.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"51a24ee0dedcaad842e4f9fce9b08da1c0346aa5","_id":"hull@0.12.1","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-dHqidXC8LHZyIcGHbxVBYml6iLrrk7dANOB/bguh5+nkYWa815LsG8XY68Ugccd3bQR8TjloX/IEC/pIDtWIeA==","shasum":"e91d26daf108f61d364b563541755d4dfc1bd771","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.1.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA8E9OBQjxheAiK+4xKSrvfSxaSx7hhhv0UC2E/FT/EXAiAZ+U07FgGTXkS9WivHG2FHFwcPZ2HvCkvHDKf+Db8WtA=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.1.tgz_1501233551772_0.8337017986923456"},"directories":{}},"0.12.2":{"name":"hull","version":"0.12.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"728f029b7f0a06adb37efde938e24d8b379edf8e","_id":"hull@0.12.2","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-X4vRa2aoGPaSPhcfAqgkS1Q7GuU3Ytd4MF6sy4ExeVNjBUBZrJLYcgD8OdA5rR2W0Ic+YrHqV2SRgwsbKf2EPg==","shasum":"22b653d9428da89857a53e3840283f7bbd6517c9","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.2.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCzKzdAyZG2U2Mcu26XicxUiZJnkfMMsUm7rOuuVOiq2wIhAIUFmHa2+mu35/11xmsLMqk/3UWb84vcnDhAWH30CyWb"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.2.tgz_1501487498411_0.9397436468861997"},"directories":{}},"0.13.0-beta.4":{"name":"hull","version":"0.13.0-beta.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","nyc":"^11.0.3","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"9d6efe67ff8e8b23efb82c3779678612be2b25c2","_id":"hull@0.13.0-beta.4","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-1sxDA3G9YCDfLCNsnhd/tsx9uBehgHMr8q/qcSaT5DtZHki5RjvSVD/HGt/jW/zEM9IOxOt0jxbZ54PBPFIlQw==","shasum":"1e9e6312efaab7d852a22b889911d5abfe809f95","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.0-beta.4.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF2LxmVXwDluwyeWtchPps0i+zPYWCc+FjRyOeWvZSi7AiEAiY2tfdzyJo0Q+PyQwWXbb2/s3PtRVWyxy3sc2GQhMQQ="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.0-beta.4.tgz_1501496498075_0.17673395271413028"},"directories":{}},"0.13.0-beta.5":{"name":"hull","version":"0.13.0-beta.5","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jsonwebtoken":"^7.4.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"d214728c15373c6322053e3b11788955f3d8490c","_id":"hull@0.13.0-beta.5","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-SJifPv1pvOm7lSlxlzF+lPHmyZGEtox/W0YV5GxPAIbLjSEpbFJFBqeGkHpDypLwpraaPjWnhB9AYi2MCofH9Q==","shasum":"c11d5d94ba168e97d2a7d7c2b2e73f0cb0327701","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.0-beta.5.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCvE54+uURiaYas+nqNJlJBG9kKIb4NG7cH/6OJUQgotQIgPUxEknvTjvXvF2PfKvFs6RkFLjaH04Zs6uwEfl3Xf6s="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.0-beta.5.tgz_1502182327054_0.8888165457174182"},"directories":{}},"0.13.0-beta.6":{"name":"hull","version":"0.13.0-beta.6","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jsonwebtoken":"^7.4.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"65cb218e23284ffd0a0cabb6b3352d5bd2da3699","_id":"hull@0.13.0-beta.6","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-aJphJ9fC5ZMXxqQirTA2cIQWQmErHOANRyMmLSK9lHhQmRC60x3Ey2LxKj7wOcNfT/LnRfhXRAQpwfiRSXr+Cw==","shasum":"9f0c004b3b2b6ba9a97851e672bc13725ed7e47b","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.0-beta.6.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBd5Ib6Iz7yTTDRSWt6NA7DWcDg+fs7xrXeG0PCX6qutAiArGx623f6FlBoydnYdTrdZsH270wBhTZDZ4GE/nXxRCQ=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.0-beta.6.tgz_1502197683433_0.25437283935025334"},"directories":{}},"0.13.0-beta.7":{"name":"hull","version":"0.13.0-beta.7","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jsonwebtoken":"^7.4.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"a941025fe04e9473970c096884fe0c137bff4c64","_id":"hull@0.13.0-beta.7","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-RAv5tRTamFDJGl9ot7wrB2K9tXDtPTOi7ccizqR5KZ8E1MBCIX6uWxYQWVL9dqOJ339MKSEfpbs/TkB5sLjfjA==","shasum":"f51bd7ec85cf67c7b3b76a0ff970b2578f4e5f66","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.0-beta.7.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGRjf8iN6/5o+wc8zD8UYQZFYqezqscNfzu44dg1YepPAiEAgos/dY08SoZmAfU44HekS+n+fx2OISLxhmhS2t/aGBw="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.0-beta.7.tgz_1502273959482_0.06482873880304396"},"directories":{}},"0.13.0-beta.8":{"name":"hull","version":"0.13.0-beta.8","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jsonwebtoken":"^7.4.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"c4f92219f073a8a00c9901aac5efe4188f4a226e","_id":"hull@0.13.0-beta.8","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-AZrn4e7IS5Qkzli44uxJcb0m1qGm6XjaF7HnsfJuOg6+x4OsZpdbApRms4wZdfJrEevFFWNLfCRuhR/L+rU4GQ==","shasum":"9400245002954fe73608ed70d4006f4ca79290c3","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.0-beta.8.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDggpwRBzBDOkZqArUpDgucI1pFy4xOxHV3HVqJSTGlDgIgGBL3mNhl6YR8y2zNzRSzDtddQ7J03NuSDmBaig0Hs/U="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.0-beta.8.tgz_1502274697512_0.5358696323819458"},"directories":{}},"0.13.0-beta.9":{"name":"hull","version":"0.13.0-beta.9","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jsonwebtoken":"^7.4.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"2cba6926f84c6936daece443dceec0a33ecc0b84","_id":"hull@0.13.0-beta.9","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-T7zfmWa3idop6ycCR8BjHxwHroTJfaWAp/lAMwvsNebxzSbxHxZpFOY7krRRkSsRxyglcHCfu8fncnW/Kz2v2w==","shasum":"a093912b7fbbd0197a574066dc200c8b18cf3ae6","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.0-beta.9.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCYbNgoWye46cxycv7t7v1SDMIBZkKylfXu5Y3O92XgBgIhANgXsv7DAMSADXv68O1+yMs6qj987hbkZLPDkzlz5T7e"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.0-beta.9.tgz_1502350512399_0.40328851412050426"},"directories":{}},"0.12.3":{"name":"hull","version":"0.12.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"10e9349b0ed1bffc917b477c4d94ab48421cd08b","_id":"hull@0.12.3","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-lJTh9Dav+SPC3hUmVbiOgUTPKALBJ8BDjUucN9HPh7weo8BCorfoncDcobr+A+RblBGPqClv60Gf1YEEj6Qz3A==","shasum":"4cb8b09fe3e88673dac29b4ebb49c9aff247c3bf","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.3.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHzs3LhQN8l50743XzYk108KXRneZtNSjCPAEHcaECfeAiEA9TR2YzDlMaos9viGA04Y01XntgMItgQk0O0NY0NeHY4="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.3.tgz_1502973165602_0.7488944591023028"},"directories":{}},"0.13.0-beta.10":{"name":"hull","version":"0.13.0-beta.10","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.1","jsonwebtoken":"^7.4.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"75a6672d6f5a0f3c515ad057df551be5ef616717","_id":"hull@0.13.0-beta.10","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-Oc2rJd/hdqzrC2Gia5S0pI6KR5vn+qeHNOs58dxmRpCkmiIVIWG5Mde8vAL09rsVrdfURmwEF47OD+nezsR4RA==","shasum":"94ccb9894a303d5df085ac902bd907c03be5a34a","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.0-beta.10.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCoHfsaUngGF/94g2Lgq60Gosxqc7GzHRIlY6HfNCFfPQIgPf7wJdOK87ZuW4PFCW0tFZDIDf/umRPd8IUEdehv4Xs="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.0-beta.10.tgz_1502996569557_0.6992593575268984"},"directories":{}},"0.12.4":{"name":"hull","version":"0.12.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"5d2d7ddedb8ce369dbab93b158e1c9d1325fa5bf","_id":"hull@0.12.4","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-sroEH5I7/5cwofQ2z4cc8sjfwcFG8ExJuH0z1RNU3YSaDylUzPFj+idrg+tyN9MV38DfvB9ahzkTNtY44b9eog==","shasum":"faa5bb6c6398d7bcbf4eb659230c3c99a82193ac","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.4.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHC6VFtX3G2ESiXHHmKqgKftSPADcn5DUUxOr/rPnP73AiBRoDRACInJoc0jkP+oixlGL0KdeOAO99FdQ1B/ssSKjA=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.4.tgz_1503163593570_0.4962998046539724"},"directories":{}},"0.11.13":{"name":"hull","version":"0.11.13","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","JSONStream":"^1.1.2","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.0.1","winston":"^2.2.0"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","bluebird":"^3.4.6","chai":"^3.5.0","del":"^2.2.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"7a7edd423c2e3556cb84cc9a9c7029d9ee361b3d","_id":"hull@0.11.13","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-0w7xeu81+4zDErg6AwIOD5u8XoN/dbGKGArid1BWKuPFyoTkIXlKRpMgxNDlZpmpYvTjcsKaDUOrx2ReDrhgtw==","shasum":"38d5e173f8df2ce3096e3cfcf815d8ad0627476a","tarball":"https://registry.npmjs.org/hull/-/hull-0.11.13.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH8dzpHGzW4ultG7rAvBG4rtq7JLItOMUY5jRoKWDvEJAiASAV+VBxLrlgKl8Yd79e2COYrrpzvqoHoC9nub4i3h+A=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.11.13.tgz_1503164104709_0.262819352094084"},"directories":{}},"0.13.0-beta.12":{"name":"hull","version":"0.13.0-beta.12","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"0f809703e048f3da629ccca6e914dacf230cc16c","_id":"hull@0.13.0-beta.12","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-OIFkae703Ek4Ymb7vjwhQzeoFykx7d0XzENTwM6WzpSE8KJ8/js7nQaPN/ew8VG4cVQr6SaZkdu8nvwLsQ0GFw==","shasum":"6551d24725200ab341328722338c1c5a3fb1968a","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.0-beta.12.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHGWQEVADebtFzbAxd5u4v9n8QGXwg7gR6SYlKV7iS7wIgcBObYAERHLn304Vjlxge48v1wU7u50mdOqPSbdzTJns="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.0-beta.12.tgz_1503656147890_0.773938087746501"},"directories":{}},"0.12.5":{"name":"hull","version":"0.12.5","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"9af8150a7245c13f09c9c97248351e3c15ecc6a8","_id":"hull@0.12.5","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-DcspJkhfKBVstSVkv2NI7ZO00Lpa5jxEig+B3RWWoZtsG4LZ6jMe8zJzBc8cALbmw5Ds3Nfjon8I46bFDhGU4g==","shasum":"99dbc934e9dc3bcd1e21148315048ac3abf0c1eb","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.5.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAnwP6nehzPchEniBzEU6iJs5oHFrEEvKScDn+CcqyTXAiA7sdJAWrjbLml7hS5cWjVmCV+gVsnJ5o9mJx3WoZhf7w=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.5.tgz_1503910450309_0.8606780443806201"},"directories":{}},"0.12.6":{"name":"hull","version":"0.12.6","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"0183b2d6126ceeaf6cdd0ce34b681426f0759554","_id":"hull@0.12.6","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-9p8daD7aL5C+GdFPAnJB8qAoZW9g8LuXXmhd/Xv+39hGwq4WgayaJHyP5tNh+CDpw7mr4J6d9CVF85CygM3rsw==","shasum":"a6ee2fcfc4a6f4cd89dd122534901c3082453f05","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.6.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDNsLrGrg2n5rkkJLfZ8VPVlJLE/kOyB1kOwn/eBu4sWwIgMw0gADT8tGp9D+PJTRcFtYh7y0BNvpAaucMJNNXpr80="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.6.tgz_1505824225465_0.2415076184552163"},"directories":{}},"0.12.7":{"name":"hull","version":"0.12.7","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"7c9601dc7cb22e67ca39a07c2a0970db9c437c3f","_id":"hull@0.12.7","_npmVersion":"5.3.0","_nodeVersion":"6.10.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-oNvVuBM5DRTczSaYBDvO9hIZdIgVNAHzEXolxwGE/uCyiaAr4j+y1txnvjGm5LImeWhX1OPlua5PpeH/tEq5SA==","shasum":"3c204ca70ed3b16405b6474a0aa06a3422aded1c","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.7.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTrdA23inO9YDkhGIMLmE47SYn1klt9Z1wLXcX98fnhwIgBb24yJiw14r7BpzTWz6j2cE40c5wseoToU+3XKsoBVI="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.7.tgz_1506432860174_0.1963381606619805"},"directories":{}},"0.13.0":{"name":"hull","version":"0.13.0","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"c5d175e433c56962ae075d0032573d5e254f113d","_id":"hull@0.13.0","_npmVersion":"5.4.2","_nodeVersion":"6.11.4","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-CwqaEssbyYnXbQXbx3QdK5NmBv4vwAZq3jSm13h7+bmzmNCiTJeOxRV9N9BvrdWubYBfScPFXo6fn4ulmVM/Rw==","shasum":"45731b44961aafc0e24d8e308e1b51cf77406609","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.0.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCTEMzJDx1FSqn45+RTM3kvEakAenrmDzFWQ9E+4UCLdwIhALB/ghFBonOOKMTKannJJ4GmcMF+br4FJ2aU+JPG/Jxe"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.0.tgz_1507717359713_0.6455114732962102"},"directories":{}},"0.13.1":{"name":"hull","version":"0.13.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"030860ceeab1215549c8a68afa5afd51f1229de5","_id":"hull@0.13.1","_npmVersion":"5.4.2","_nodeVersion":"6.11.4","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-a4F31PEiq3w6KDri5OaYKintO8j7sL3enBUnlxZY6sfnIriKwgOh5IsZG+FCGVcKNpxASoUuNCVn9GbNbkNSGQ==","shasum":"170f5a72bd331aba2f35f9477f4e312a1449af03","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.1.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAQ62F8Q2yy89YEYfDXSyzI9+6uH0iSN9lQeMEwGOGB1AiAdMrOQJEy1OXOjgExRvwLiSPBs5u3bqC8CKNQRpp4IhA=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.1.tgz_1508168208563_0.16557781770825386"},"directories":{}},"0.12.8":{"name":"hull","version":"0.12.8","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:units","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:units":"NODE_ENV=test mocha --require babel-register -R spec ./tests/*.js ./tests/**/*.js ./spec/*.js","test:flow":"flow check","update":"updtr","clean":"rimraf lib","coverage":"gulp coverage","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","restler":"^3.4.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"aws-sdk":"^2.81.0","babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","sqs-consumer":"^3.6.1","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"gitHead":"013801e361875797456c47add8902a52d4534e24","_id":"hull@0.12.8","_npmVersion":"5.4.2","_nodeVersion":"6.11.4","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-8K9kKGyDDyRYeNLL8v++OZL9AIEX/RNRGQeIxedyDnq1SrCW89pqlsrQOCYBe2E+I1CclwN+l75qy41JxDVHqQ==","shasum":"70c3efa522bec8b39ce9898ab3d6969445cc64cd","tarball":"https://registry.npmjs.org/hull/-/hull-0.12.8.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZowFIFsQbUqqK1LMD0vi1GeyuN3fVMSXtYMOiZNJLLQIgTmNj0iRitZVT90DE1Z+9b+YEMTOVa8bV/mHc7XEcVHo="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.12.8.tgz_1508168252138_0.6606107181869447"},"directories":{}},"0.13.2":{"name":"hull","version":"0.13.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"97a2804cd663c1c3711f41d1bc4c16b87743b736","_id":"hull@0.13.2","_npmVersion":"5.4.2","_nodeVersion":"6.11.4","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-43pWCCyMs7jgnH8svJM6LJqq8BquYxh0FVc4DeXh7Yxic1ZM/fNaKfawbYZXQGxGyLB60xMsSpHCEnSLtYa9tw==","shasum":"ce03fb46db0848bfe97eb67e9a005c3c5b5a4541","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.2.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGsTaxnpW0Z2f7bln4OkkDVEda/2rzZgsKsXv5ri1fSFAiA2xBlBImrmEQDhhE9nY6B8bJ43O5Jx9CDk9J+SdTJhhw=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.2.tgz_1508247724353_0.16139927925541997"},"directories":{}},"0.13.3":{"name":"hull","version":"0.13.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"726fcdb36c2561479c7d7eb61967a18fc133bea4","_id":"hull@0.13.3","_npmVersion":"5.4.2","_nodeVersion":"6.11.5","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-Gjqv7euy4qUFB2SAL9+pjEOFR1zDtCLqUI7DvGKKNZQ2iIEVyrwP4KxtDKWWR/4AhiauRVfRCpj3gh/CPhqQlA==","shasum":"cc8a00bb504251a8142e4cbe9eb80e04dd4d0464","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.3.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD7932Z3p72nOUUpgiCGCKiurfjnqf6h2F5/pMFsdFQwgIhAP81vK8NOgT1v4TEoG8ICYi3C+SDj4xiE2m6I2zIBCig"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.3.tgz_1509721075576_0.5251005087047815"},"directories":{}},"0.13.4":{"name":"hull","version":"0.13.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"f260d79f7f9f502b782bf67b56b49ff28f4ddcf4","_id":"hull@0.13.4","_npmVersion":"5.4.2","_nodeVersion":"6.11.5","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-RJ2w/6W/tS2yXPptvj5zT1rch/ZISGu+kMm4dDAbgZirw5NoNrvyNmRllPruiKd/LXRVi/gj9Dp5x0G2PmmJIA==","shasum":"4e90a77a67a39a7b43eff27402dfaeece5047069","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.4.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDlyVpb7G+ODeSvSoFsJRKfjtdwdO5ls7bIXeCKRciZ9QIgYrCCZnXb6kC+nTGJe0fdEkd3M2jAIFsjhhoBwiAgp3w="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.4.tgz_1509724414707_0.021247383439913392"},"directories":{}},"0.13.5":{"name":"hull","version":"0.13.5","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"11e104fc440f78a03e307818692fcce62175fca8","_id":"hull@0.13.5","_npmVersion":"5.4.2","_nodeVersion":"6.11.5","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-MonOPYpueUO956reV7zTyr6rryoWcJfgXtYbRHp6fGUov7hS1u084Kub6jEugrFXOdZDilNLlE+ld1X2f0HsSw==","shasum":"4023e33a7ba147bd354b6ce02761d07b17c35c77","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.5.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFXPwMaqrQxJ+GGILPfJO4AgPBRjOQZTC1YvXEOfkAniAiEAuGj3gW3F+NJDR6SnXiZJgZW0jWRXqGMejPAUCXN6v+Q="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.5.tgz_1510141787805_0.5844253259710968"},"directories":{}},"0.13.6":{"name":"hull","version":"0.13.6","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"gulp build","watch":"gulp watch","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^1.39.0","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-lodash":"^3.2.0","babel-preset-es2015":"^6.9.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.5.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-import":"^2.2.0","flow-bin":"^0.45.0","gulp":"^3.9.1","gulp-babel":"^6.1.2","gulp-load-plugins":"^1.2.0","gulp-mocha":"^4.3.1","gulp-plumber":"^1.1.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"c0a0e50805069aafc3367714ba2ca754c044c51a","_id":"hull@0.13.6","_shasum":"e09643bfda12ee42e29c605cbe4d9b196de9f3c2","_from":".","_npmVersion":"3.10.10","_nodeVersion":"6.11.1","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"shasum":"e09643bfda12ee42e29c605cbe4d9b196de9f3c2","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.6.tgz","integrity":"sha512-wMMSJcg5xjauA8sExxK2HyZyOpBQxMUNWW1Zz08+V78ayYirAAyh0pS3+Sb1vv6/NFqgRm1YEkvk4eQs5JPprA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDskvKyvXjkd4kOfedIZi1RnuUcWNcVMLezW0ZjqdFJ8gIgeWmf4ri8poLT2qgEOpxTeLjNwzJ7+WbS9GPeWxy810g="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.6.tgz_1511800343102_0.7104045196902007"},"directories":{}},"0.13.7":{"name":"hull","version":"0.13.7","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.59.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"bbed556306280e68be06f36bc96d614dd8bf46d6","_id":"hull@0.13.7","_npmVersion":"5.5.1","_nodeVersion":"6.12.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-PvG9Ai7Ao1MGtqzBSeBM65IhkVPfDQZerbZMc79La9Of8iYZQmlSDJeA1Az1F0n+aQUDJJXmAaAt07JkgCzXeA==","shasum":"024c312a660b981c979380ea629fa3c80936877a","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.7.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDqmG+kEGagpxZ5jDQ4vP7kbNjSso4JN7L47VmyhDJr+AIgRs0IkL4QIK5O14CTGsa9XZORWq4iM8mJjx+Kfxqg90c="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.7.tgz_1512658974455_0.4584259914699942"},"directories":{}},"0.13.8":{"name":"hull","version":"0.13.8","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","express":"^4.14.0","hull-client":"1.1.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"aws-sdk":"^2.81.0","sqs-consumer":"^3.6.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.59.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"56a277c5350018f36d8e5f44edf0c40a41c934a7","_id":"hull@0.13.8","_npmVersion":"5.5.1","_nodeVersion":"6.12.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-MAGHtVuE5tjqzGJg7u5KOButVo7bw6NVRV5blUt6KHd0Jae6ODz8+50q8LrIDsnFbzT9g5TfA/QxiJHRm3/Z0g==","shasum":"63afe43ac290349d5f7deb6d536f9ccd28a6bf29","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.8.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG+bkxj36iymIYNG51dPkZ65ansjaAWm9c3HERLK8In+AiAwqBbdkGZh7yb5k/OhQSYWc8O6eb9Pft/pVdrF775YdA=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.8.tgz_1512733706795_0.5382902375422418"},"directories":{}},"0.13.9":{"name":"hull","version":"0.13.9","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.1.5","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"express":"^4.16.2","newrelic":"^2.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.59.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"759cf09a50ca291a62fdbb512fe61e10f49b2dc8","_id":"hull@0.13.9","_npmVersion":"5.5.1","_nodeVersion":"6.12.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-sIXnZEc70q7aFQQ6QFQBg8C5ZXFeaaAQnqhoUlp2/h5HMWs8v89yhag39s0LucG9jQIYFIvDdYbCRBj8aXEpHA==","shasum":"0f3aec44ad2a5d424b58d9fd35617a70aa34b3e4","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.9.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD0rV1smuG5depfCFncL5anG/DW5/7jld/D4bKTg3/SggIhAPxFkuMSdaw3Wkh99OKly4LGP0jT18zLywgfZYMUxJxA"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.9.tgz_1513072078659_0.47584902169182897"},"directories":{}},"0.13.10":{"name":"hull","version":"0.13.10","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"^2.1.2","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.1.5","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.13.1","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^1.1.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"express":"^4.16.2","newrelic":"^2.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.59.0","isparta":"^4.0.0","minihull":"0.0.7","mkdirp":"^0.5.1","mocha":"^3.0.0","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"6d19030a8ea416a7f6ff3b4c83bce1f4ed50d09e","_id":"hull@0.13.10","_npmVersion":"5.6.0","_nodeVersion":"9.3.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-AGyflBrNAYvtGs+PJ2E6AbqyBITjQo3ejfsP0C02pJPlReEN7W09noNFE/i1X52c/ZrJwPKYBLJnFmZYqHopGQ==","shasum":"7c9f55a7e3bca13be55eab5414973162444a1acd","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.10.tgz","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCzFzABQuoWNsjOKzjlQNHIX2TWKEhrVaslA3C2kdH4hwIgQ5hLOLwfY0UXyjPhdxctoZ9cBUFto4j97LbxEFZR6r8="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull-0.13.10.tgz_1514364379883_0.41809678613208234"},"directories":{}},"0.13.11":{"name":"hull","version":"0.13.11","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --config documentation.yml","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^1.2.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"express":"^4.16.2","newrelic":"^2.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"c50c7012d0290e38f34f791b390846f2e61d1e54","_id":"hull@0.13.11","_npmVersion":"5.6.0","_nodeVersion":"9.8.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-YO8jTnlb3J6ccuUgrcX6gLWocycEX5OsTB1K5O0Eh9ZmvPziojLI7i7zamqh/V8xG6yl4r551WEJdeGVbd6ATQ==","shasum":"41aa97c38e5e3a12c071a25c90a707e6d802f127","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.11.tgz","fileCount":144,"unpackedSize":1113416,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDi12gwjCkxxILpeBEmMXWfwL1g6n0wqRxQEw3+aDvxYQIgIi+TsOiFzREUe7Tltzf5AOOJSufazCZSHD6E5aTOpZQ="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.11_1521737425050_0.21291291327087825"},"_hasShrinkwrap":false},"0.13.13":{"name":"hull","version":"0.13.13","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^1.2.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"express":"^4.16.2","newrelic":"^2.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"04fdd86731d54238a330ebe4862b719224df6e24","_id":"hull@0.13.13","_npmVersion":"5.6.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"integrity":"sha512-ApB+UsGqdrHrwMC6EMjt4KtN8Mu2821bdemgIcZC4Zj9QwWZ7NuguD63+j4MwvGoY7wPtlum0iCIE9JUVWnmJg==","shasum":"fb6ae3c5117a72fdb896056005ef91ae9ec30944","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.13.tgz","fileCount":120,"unpackedSize":563932,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCjLLf+t8oIhAxecV+16+REMgy5/RQkjf8vP0a6UCd/GgIhALjoJxHXMQBr4lZjZcimkBsvYVSwPA1BPRa+Es2wZbaU"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.13_1523636292376_0.908698350592533"},"_hasShrinkwrap":false},"0.13.14":{"name":"hull","version":"0.13.14","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^1.2.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"express":"^4.16.2","newrelic":"^2.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"cb2cfd52af828a55a1402c44ff9176f9925b6ca2","_id":"hull@0.13.14","_npmVersion":"5.6.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"integrity":"sha512-vtaOxkVox6U85kCFcclKHxHgpk7UsYQRB/eTo/aMCzM52d7DDEV2nILSRmRvdjM/pN7b8DsyJysAuquv7ZJyrg==","shasum":"daa5acc5eb50b80f1a04fc49fe42785d44059a88","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.14.tgz","fileCount":120,"unpackedSize":563983,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBqaMHrAsWtYr9uiJvBsMSAjGbw6swxLByPm4QWaAmfmAiEA7QwVMXbacRWGVhYRHuVZqcfkEG3qgV2DndYAGb5km/o="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.14_1523637250088_0.7813670216284507"},"_hasShrinkwrap":false},"0.13.15":{"name":"hull","version":"0.13.15","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^1.2.2","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"express":"^4.16.2","newrelic":"^2.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.6.1","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"9891bcbbfba8aff1dd31df8c301be6e34f30233c","_id":"hull@0.13.15","_npmVersion":"5.6.0","_nodeVersion":"8.10.0","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"integrity":"sha512-DrordL8Ar4h8ycja1fmBeEhAiN7MaGZdhaFM4LGA2d8g0YpTFRI/VlPyAFM/uc1Nkf/kN9Z3azIyhjLm7JjM+A==","shasum":"af54af848690d3821f928fe2290c60c4fefd5589","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.15.tgz","fileCount":120,"unpackedSize":564076,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbBZ92CRA9TVsSAnZWagAArWoQAJelbzvf03HVSpJZy5cE\nzZ5kJB9Ssz7x52kFpyL9OMSeYtIbOtgdcihBDs9tEO5lK/4W88OISrtGvty2\nL+fhXzhosSCanfKlS95EwpimM2fCh1dIhWSNwybzgk5H7gh0doeG1BpskVX8\nBqDB/RZGWjHd1Nnq/oxTWuDTNu/9tXdwl3QoAHl+E590aDwuw526h4lsCqre\nNrQyI7XuZJYvs4RJ+RX5hwWZyxlk9LezqD/Qgxi6btgEDlaGeWitfzvu4IEM\ncI4TdUmo2amt7ePqpLgkdKvirn24qiowSU2dBeJFg7UkGPr91YjsKAjJqmmj\nkCMlkE8qRHvAhVGurIeFV2UMSSdqmr8sZDS6GFm+IT2nvHmszPsc2tDhRGu4\n2CrKsw5i+6Tdbt8A50nEsLQRj4OJx+LsAX8OdZCjlxE/CR6macpuYcWtIYuX\nIRXB2NbZWyPqo9+CFrM78TtlZSoTRp3PrtB1/Xljy6W+SCk2J35vhZVhXNWL\nJPybgfZxQkaeavH+OKiBViMUPqo1CSEswOc6FwrFEMWnONLlXBp6CiK7uD6a\nPXiO4wN2/81ndE+GskLKoG4d0HIK0cdENWnXyA+u8avsBcTaIZv+qZJ/8Zzh\nbWFzbHiSUnLTvlOEpVqYMg3SMSn11Zod5HOYg330qmtPjG8j3SvmTf5DNF+3\nwEnu\r\n=CZth\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE1bzOMCR10D3O1CrvZ2FUSxcCcw5/FDaeTfMvGu1B4mAiEAp9V3e6flMSL20RExc+5rEdl5z+TCqIvcm3NfK9SznWc="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.15_1527095155122_0.2634188570982796"},"_hasShrinkwrap":false},"0.14.0-beta.1":{"name":"hull","version":"0.14.0-beta.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"2.0.0-beta.1","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"express":"^4.16.2","newrelic":"^2.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.71.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  message: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n### **message**\n\nOptional - set if there is a sns message incoming.\n\nIt contains the raw, message object - should not be used directly by the connector, `req.hull.notification` is added for that purpose.\n\n```javascript\nType: \"Notification\",\nSubject: \"user_report:update\",\nMessage: \"{\\\"user\\\":{}}\"\n```\n\n### **notification**\n\nOptional - if the incoming message type if `Notification`, then the messaged is parsed and set to notification.\n\n```javascript\nsubject: \"user_report:update\",\ntimestamp: new Date(message.Timestamp),\npaload: { user: {} }\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"36b7f8e5805f785be477038eb00cb235fad7f57e","_id":"hull@0.14.0-beta.1","_npmVersion":"5.6.0","_nodeVersion":"8.11.2","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-O1q11hA9f74Yg4Uh8Pu9MauCC4r9GMd0iHVnCX8gxezUceIIgcIv8vqwBL5Np2tSlrHqiirrg6zvoFHzMKaA9g==","shasum":"7d705778bd8417cd34e0fb013ee6bdb5d9f65c49","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.1.tgz","fileCount":4315,"unpackedSize":94174280,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbB9t8CRA9TVsSAnZWagAAC4oP/R1GhHnDJOBZZSsIfzVz\nP+nM1dqlSMykp2JhFs7H5pmOMYW/K+6oHGlGlB9tDV7rTiOJiHmdZmY9eleJ\nHNlAfDGii2MdXrgv7zMxkNPe0s8oWRsshY+6bVcCQRNGDb3qf1CE3GrjHZK8\nIcJDGRjUYHYu6uXwX5uRCwqbfcNONEKKV1X+LcRh29rraDKArl0poQJq711r\n3LcWEp/GWNTmtNdoZ3XPGTEJue2xe0JOTmBOQGZ6RilRyW86MvJT24gB3D4s\ntyfR/kjL8BIPjs6k4gkyocIRe7WRmRRq6y+qLrR/vaI0I6C/kqEKs/V1qvG1\n+j2wmLZ/0vyVWhs5iyjDlJMIuGdTQYsL5sec/HmCELaYtOaaFP8QH7PyqpS4\ngLd6J8ukG4vlQujnTqjQ2gnHKOl4Fus0yMfCvasGGH75CuDPixbcq9NhqtCO\nItdwQDW62t0wGooY6ECSARE8PcCOd5ukL6ACC8YW4ZFETFaBtgSbr8X3CXmV\nfwgH/7bn5GqLE6bxZ2AiMST+Z9jw4etRs+jAGATmZQ5DIQ96DOrePzFZtR9N\ntMYBk2tYtzEgUmqYuYDlF+ZaGHRNGcGK7FI8RWT9HHerWgB/uMZieiEqaODM\nBiuGA8WU/C7TXBMHEX9L9Dv2nrlOY/fz5mVRxS8e/jDIUFpIabE0RAreUJqy\n5/ui\r\n=1p45\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCz8kX7rVmGA7NEd+EQyUbQQKAZnxvYJcEhxgJ3flf2/AIhAPR/zyI8XXvec8KXi+jTxI8oPWyb17kS3cCnDEBTh1wZ"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.1_1527241593476_0.18475625073335356"},"_hasShrinkwrap":false},"0.13.16":{"name":"hull","version":"0.13.16","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^1.2.2","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"88dc40190a079238254f1d64336a79bbfcbc85f0","_id":"hull@0.13.16","_npmVersion":"5.6.0","_nodeVersion":"8.11.2","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-lZnx7O1MCJx9+yvxhxGs3IYDoVlWDP8Yygt96gY9bJzFmNVOWO8pvyjeOUOtXeNNcPoAe9MyMD/nqWatG5lM2Q==","shasum":"5760f0de3477e5050e296913501a46ee90519a50","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.16.tgz","fileCount":4336,"unpackedSize":94204824,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGO37CRA9TVsSAnZWagAAmggQAJdIXP0/PlsM85P4OMPL\nNa9x12pNAqYxOCM6CHIAXSSR0R/ekrRBReiKnJfsbQ/klqFkhfG/vo1CuuHS\nViephUtSuX34ysZ1p3eMQugyVdDrT1zZrzQtIbvRlN+TouHwc6IJ8HC00nkK\nVduyrkeXxoQOdiBlHMirGATFMBckm6xI4ZPP5Eu8/8Y/u6omGQ/0jvd0ToRQ\nsA/m0k9B49k4z9lr1qXQqGV1jlvWz3I76urzk1S9Q3hftgPy0DZDyM86M5+B\n/3b2ZUuu0ZXA2i//T3QBTs2GaUOmxD8BbgVXUr4PPmMPC4szEwsxhcX8bVlE\nGaHgi7AGvSWTSYXEaSkp3AtyNVRAFoLY/TnTSlx9vFRA2wag5a4bU6sp3rMj\n0NzYdRVDcnxQJClxkvH8lVWW7DpZzl/x1nanZzs7ukTzmayJab4I1gutLgYX\n+igRA3pBjxNX1Xy7BpdMGCZner9W4H7f6cG9jMnW1yZSYYCe+YjXnuggxyqX\nG8ltHrvk0Tmxxcdn5Y40nuPfStE2kjT7Yt+A27fHbQbMp4uRNtXUBIKZpxNK\nucZWPH6GQ8pQTc3dydteD0rTgm1xYqdcpLv0HDVGUFh3olTtD74qHdjUfjiv\njyDYleV0RGiQfnwohsHPjsXQ7r9zuzDuos+xAy4SYPC/9H8sSN1kOttangLE\nn3Ym\r\n=8Qs6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGRyqru5/x8BPlzuABhgDjnDOC2B1jfroYaagTrfDudOAiBACWba2HICldBe9Utr2TOGQgX1XnDpoPJiJn3JQ8CmOA=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.16_1528360439991_0.17179092131983564"},"_hasShrinkwrap":false},"0.14.0-beta.2":{"name":"hull","version":"0.14.0-beta.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"d199c32e8d8d91cd6d3369fe0a1a99fdf45db1d4","_id":"hull@0.14.0-beta.2","_npmVersion":"6.1.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-daWmU9Fz/RxxYBpN+kDdnvRcKHRpumtFkOaJtWxL5O6O6MkkmPgV7z4RlayQ0/ILximuCFpDBWMzEtf2zfAgog==","shasum":"9010a8ecda6cee50a7faf0f5bd429b25a8b9eeab","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.2.tgz","fileCount":127,"unpackedSize":1391277,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKmaNCRA9TVsSAnZWagAAHPoP/3kD52YC1fkteTjQoh/q\nZ97zJCe3UBCklTLbWe6ggDEsYGSO+1pTHfkYGBvTfl3gpkg1bN9z++X2mS8q\nMm97vviM7a9UJfYaiGu2y+tcTiYi5wsk+zQH9oyMLl/yid6IhlOWNX0UBEqI\niWDZNozbFj3R05wN2tkRI5sCiVW1UiPbcIZIg790w1lf0Z7b9JuGjG2Ejfod\nx6nxfny6ygTn/Q0810h5N3703Y1uoN4Xbqj/82Tzqjd7+JKT5U/YRX9pgP4N\n3D87AI+DRX+y4dcUkQWD5toc47eGzc7ywHfzvsi5QFetRFsGLi73fPJhXO/V\nTM7EZThlC5taKWp3DogI8vTbQiXDN/Iysw8b8CWenrHQtV8wCsz/WkelD2mw\nG1/NLVNbXFRqtS2x/NnMk15rJUMCTA/Son+5/1Cdz5BGDwNPyWmxnGA15Wlj\nzX3vG9UmhvzC/DtbvRcMi1N4OtboPzNdYxgWnhd8yWW37v6h+kJi06tHJydp\nFblAjbu5NwEXAYrJvtCrFE2UzWvZpVPBzOsGTdOIn+a/ty+Pm88trG0CoUkt\nRtmIDeN+BEfLgAEq0eZi2dldn1kFOFrQ3W9pf9CYrfvcj+mivaAvGvJ0XMD1\na8soIwq860XiI3uBPqrnL9ZLcZzxh23Y0Bt6IFpyKyDNGo0jUgZZFwDx2t4j\nZgWt\r\n=jg7L\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDh3e82azW08X3zZwtBW77KVW7J0Ecgod3GxptpDHfWQAIgCMhoQtcuVYNfn9HEFJhqtzcKEWD5ZmRQ1pQtlNP4h6g="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.2_1529505420767_0.14929703676636175"},"_hasShrinkwrap":false},"0.14.0-beta.3":{"name":"hull","version":"0.14.0-beta.3","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"70b5a9ecc2eac760b08189dafa2eb1022118a9ad","_id":"hull@0.14.0-beta.3","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-b5fWeNl8PcXM8HKPvj+Q6KiuSGD48RzIN8wxWRaQ41txoVIruSoNJsJziERs7Wn4kkfy4PpRaD9OeYo/Ym0NCA==","shasum":"e24983eb4da845446fcb7609a8b86f67b6349c04","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.3.tgz","fileCount":129,"unpackedSize":1393277,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO1I2CRA9TVsSAnZWagAApMkQAJp5DF8SxAR67LSCChTl\nvJGUDSsdPsPelJRKu4RxtRqPq1fuX/RMFGCKmaDibEnDdTJVvzyFLvfgZsde\nIyJ5W3rI+7oPlxpl4S1Vv+YgBEmSFZzyNzmMEDhXCtGuyiBMkXiJTjs/S/lM\nV+Kw4x7vk362qhbz0/Zu8sjMZ3zHg4oWAwCDfx3s5dzo6Hx99Ev7OAUdKmVR\nmLyDjjyeNbAark4nrOJt/iiboEblh8fr5HpBvyhtAdTaIiCK96aF0sz/lYjG\n3+CL7UNpmlR/nHKPR3zl0BURlUTJWbhq82RxjfU+JHQ7SbpJUHp26XkN53TI\nTiE7mKgOlpQ8FwmV+PuEGYu5aLO6qodxeaQZSa2mszTjxqlw6h6f5/KWSuwX\nWERisK9mVKVUi55F+UiCP4PGn7D6v76D+xAJ1WNVGWVzrKUFjhiy7iGqcrg+\n5VmDJxlZc9whgTOXnHzRT/gFAby+lqeUv+eFrN+0EyWJy2KVrvAV83KSyLT6\ncm+3tgdbmqkwiYgLGwAy5URtVsec9Pg35halqD8MmQXQ68kRP2Su4HWgGCwR\nMjgytuKnJPt/lQesfP2u5rZ0PnA0Rwow2lgLWBCCLhWt0z/lxlKyHGx87xrP\nDNatea7Yyc4WrV+IkGpx3cuX4Z/Y0mJyTfTrhKsKq6rSEHMpXbyIzo6V2xgp\n90gj\r\n=ZsvK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDLlLqE4bnZpLyu44Tk7yT/Kei5J4TKf9XXWJk8ySWLYAiAP5hQT+uKbiWktKU2Vhx5LUF4Xj0WmNR+HcRm+4E6UZw=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.3_1530614326041_0.5753647550750776"},"_hasShrinkwrap":false},"0.14.0-beta.4":{"name":"hull","version":"0.14.0-beta.4","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"f0a06935862785ee488183b2c2f2df07a8300ac3","_id":"hull@0.14.0-beta.4","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-Ml2ln14p0hmrbweoAXrDGUrkQJhIMZlMAGUwzcuymiWgIcUV+ihUtTgO9mRP2p/9H7yfg16XnGyIxLaX8wJj3w==","shasum":"10dc59923ba8cc127ac86be7b94ad19cdb41577d","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.4.tgz","fileCount":128,"unpackedSize":1102789,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPJVJCRA9TVsSAnZWagAAYEwQAJpS9O7hLB1h8gqMfiyS\naSbobzfl5WYgMl/d5dxTiHVK0WzbzST9DqwstZlKyGC2ULA/TM9h4kN09/oR\nu1R5S95n/58IES8PjEC7TJtZ0t9kzT2v+lC1JyHyeDKRo2CvMNAx3x8WMb4z\nFmGnt1jbzuHq+ycNrnWO18NpEmogOBj2I6v6YQo8q2jhIhWLg6Po03o4+0la\n1q6s7nH/VkMoupzOef5kGfNcHexJ1heOGKK80EqWgYhOX/2ux0keoWdJkKwP\nccMY/+bctXFcXDi/bFZ2Uk7vfr5jq/ivp6MgeugdcemFqKqutwMJN0sb24qB\nl8ph9Km6yZM66d+VS0xCp43wMbowAIqKc0hjYztcW7jOW3Zofr8eJIZY+iji\ngTrRo4Knju5Fqe2Lwg2QghtfPNXuaiCXoxZ0l3NwjovI63RpNNIm33CXtJbm\nsHLxkA+l0jCM6+0wndAit08sFe11CF65eZmrVq45ZlDeUtUbygZkE1kEWKZ1\nBjEt/EDB/sO05AB71f7o3RmURTQDkhBAH64vnVATWHhUNUaXbyw07Oh+uq7r\nwnpnSlf7IXlmITgXxS8UOUiWTpgrX/bfHp954hTamr/mFpoXWr2V2VaKGPXM\nyr/AZOAAQZ+XnM0vS3MsLDrmLbhEmAn+toQbeuFyjAchVJIm7TwUW93k2cR+\nYp5f\r\n=0e36\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICPgADg7oqQR4QPwRl7QDQqmsJGxo89fYb00EIcfDlfvAiEAmOXzD9617kRpyrpQyaJRImbAHhXVrw9ju8yD5BmzJhI="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.4_1530697033890_0.850581351491345"},"_hasShrinkwrap":false},"0.14.0-beta.5":{"name":"hull","version":"0.14.0-beta.5","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"28e27a69eaa4d98a7605a47e9388add03aa7dca1","_id":"hull@0.14.0-beta.5","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-U2VE4GTrHqT26qZ9WRWb0c/P08K+NPfVHns1zOcUKNlP2jYh3fVnT+Nzc4OdvSbcPfaT/w1GT7Ni6yNm6QuNew==","shasum":"63634cb67fe7044433df57d0fd923bf840dda51f","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.5.tgz","fileCount":127,"unpackedSize":1106627,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPNZZCRA9TVsSAnZWagAAchkP/3Ksx422tBf5zSCd/FEA\n47k5q7gcrwrd8utSAZfJDajq7LPOO7JUJYdrMv5nj8UMEG1lF167HIRFts5q\n+N0/sfzLcktU+YQdgHdDZFOYf/nUC8cBfUreX7gFFkMpIx8TVg6fuD79pjY0\nnq0m1BwcgIHOBTGMJaTVBkeh7QjMTJky0Rt9TRF9bLMLe5G5bXtGmYWxdcsH\ncmFgoTzpWqdjFCdgwOB6xJpkfon4RwGNAWw0Q46yktqUJWWPsAofeCwdbbff\nLUWQHgieJ/R1K0Tt1a7pCzhopDz+NUeGDUJbCeYCnKO5Fhid9UGPUMXEzqgP\nyJVwVvc84hszWVkATCiTwvmgfX+RZn+plx/ZObEm5dMg1lt8qsIS8IPmaW6j\nyU1c6w09rtGZTyxPUOT7naP7ycrFxDkGK82aMlaylUn9QaRiE2U7MkAuvX8S\nuzBA4dhPQUawMEmmF6gDxCvapg01ncI7ZOcOUZin10ZFbZgi0rBBnZkoYihS\nPJvT7J1l4OB+8qBMsQvMdIebnsFbfrHnLazKa/eFFPTseWJ4nAcf+1u9js5v\nr4X9zh/0nvyMDTA1XhUY3kyDWpjcVEp1WHVwQOufSRb6HU4+Rq0m0jSTkdLW\nLiJ2Kb8z2MhW+qkTBgbwMYHA6owmBGUM9b/VIvhV2h/zv7OoJ2O15jg4yCtS\nSSHV\r\n=P7tJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFeR4aKEyJx+MUvPjbokMPr7AWB/wNIuDJgCYuWxS1oEAiEAqZN1vh92eO4OaFzImWpEhKgnviUCP3oDhgr9g2WRguE="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.5_1530713689443_0.510060356595178"},"_hasShrinkwrap":false},"0.14.0-beta.6":{"name":"hull","version":"0.14.0-beta.6","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"9b3ac89ced606845dc7d01066e265bd2b40fd4a8","_id":"hull@0.14.0-beta.6","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-bx3tvUVZjmFRR97AceEPE/8lCRQ3c2nHGHKMcunmybZ4eHjn9h/Fl11q1p+IdSgR9yJnKHIYfl6ZA8QzW8i0pg==","shasum":"ceb1fc1dbf9a27d5048b802aae2f2c396b1ebe6d","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.6.tgz","fileCount":128,"unpackedSize":1110992,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSHulCRA9TVsSAnZWagAAcnEP/jMDEvg6esU+KXmZud4K\nBiIkXLKi3eP5mUMOtLW3cNOup2zBlXw488WhrXd3LJuj3Y5W8QTkktERK/o0\nZiY9Qty8X0DRVotkgcD+dhF6fOlhm3eWqILHYq21LaWKurRXA220/VvtR5AN\n8e2J7gZI0Wh24VJoMB2SFxeLZ3lxQpIG2rSHQ/KGK9Qv5v+umMf7SGXAxn1v\n1Cz0iP5TnGfehzU1MBoTM/cIx2W71t+hflOwB4ZAYcBO5DylTQWgSfVcqQzF\nLpJREsiAfdtx+gAj1hm4FGDo1LwU68Mm6zb6fBUSJHUNe8pWs1ySkPlmMFo3\nuNBp8CYg14y9TmXgplUBYuBYmqKGMR4ydFn1bBwJdgQ+h53xvwoflYVlr7RR\nQr5sLGJfPCV/vCQauVOs29JRXQmRoGc/crHpfYVPRupJuAwxwC1elyAAph8k\nG+XLGqkSQlzXJixJv682GW5Vu7thDts18sUkCgjj0Z/gejb7CfBFlYOx0jMZ\nv211V7vQkPSbDfRRQDenNBi6+Rg9oBrIzIv0TG32ILndpvR9lnL8cDUIeP3E\n+rh2bTTrcWnyf/BzBccz+cSr/Wyafc6vSDJ/o2Z4JOB3SNRmGoN4Rr+78lsY\ngjcvasota05ZD6eEOhqh0nMDgpovSH5H/S7JuHSuV4Tmf/MCJAX1hMY15m+M\ns4QG\r\n=wEnI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG4AwmPzJJAsH1DdC3pKarr+Y7lDHlvsRTBx21ye5AanAiAfdAt5fwLjOZqni6BkfaDq/vN0b/B6yBxmEDkmHgKGOQ=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.6_1531476901682_0.5943592098226105"},"_hasShrinkwrap":false},"0.14.0-beta.7":{"name":"hull","version":"0.14.0-beta.7","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"d9e39fa73e261661b2bd77c33b78dce2c16e8467","_id":"hull@0.14.0-beta.7","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-z1QkcFvf2rqmIc0Br84liuWG4iy3EowfX+2vJ64+uJCaadGv3oDLO7mmzEUTLWQUNnJQ8eowBWgrhXttnWM5Yg==","shasum":"183371cdb531e59fd905e8b3d67b6fae8e16fe68","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.7.tgz","fileCount":129,"unpackedSize":1105809,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbTzZsCRA9TVsSAnZWagAANqgP/0lzPK2HGH4wJ67z/ZjW\nN/qcbuul/T/99xTrAfPOM1trDKOuvkI1snsyoCQ8QXAii/1DTw7iRZKf8XD3\nO7R1jjcbpXQrHegGYBV647A1AyzCRcJNvb11D5k6SCHuroC05OnQYIaVwEUw\nWUP+zq0RiOiOkbtVbJULYmpP9+2RKpyr2PontRBLkIbJ57ebGZFVHj8L8yPo\nUu31r6B3JUdJf1Pcmhbz8hvsii6Ynp8vt6cnfK07pZBahubiMNZ1LwQmARUi\n8jL+haZOPyw8YdDhmW5E+vGK4YAkn/mHv6XvP23jhu1uFeMROMGnM5i1M8O8\nJ1FUNJyxOiUnSBGTwgy1jSAUD96xJBgKWB58mkOWjNYP/N81/Tq9bUvPPyDS\nGfG3BrhxBpFfXLLq/aJrdKRDwxwfTDiEmGRu7aSZd6m7v3pniemo1leSj3FB\nRTJcq7tLe52yOrRZ/faS4heVSUYZqzD3/Tkrp1UBC/CShrBxwA+ylMQmaB7q\nMy9SV3CVqXin7PiDJLhFEPp+05wOu8WQp1KDTjlARxkBiKSKtGYR0tP8yiO1\ndqj4zwfD62hd+0kbQbcx5+Sg92yVocTspooWWBeBz7UoN8KQphTw3yHkoULz\njvWauY4ibtmMBD+g0XR6vL6IXfchTtzClZ6lJpdh37qlRPsfDZr49IbqY72r\nzRgv\r\n=BfW2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHQhS7vZKs4FuEy/RORvO/uZxgh/HnLc1CJgCsJu70leAiBAwvt5TwQJIS9/t4n0+dkqZe2KzgoQCzSz5tZYL0uEOg=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.7_1531917932419_0.9229571779871542"},"_hasShrinkwrap":false},"0.14.0-beta.8":{"name":"hull","version":"0.14.0-beta.8","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"1a7ebfa771a258eb67b1ffb2531dfa8432f7cc94","_id":"hull@0.14.0-beta.8","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-JsR45rzTPPokSt8oAXZ1HG4cXm9RuqA18pY/J0EoJjvvt4h3UAj9lkmr2u3UcI96/47HeJlhBXzIx+ScPcW20A==","shasum":"2fbd3c0327b41feb49f79373aecbc9d2ad22eab3","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.8.tgz","fileCount":132,"unpackedSize":1103698,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWGIiCRA9TVsSAnZWagAAHXwP/2rmcn9QC+PNE3lVpz52\n94YbRJDvvjD9k13drc5Amze1iXq7/Cy62ZZMZZG4ZQSpTz/xRVQ/OOSgkNx8\nxIykC7qOPkHnzDZySyuiiabiMaQLZ3ypg1FyeyU4VLPR/n2esLLin4YIHPTz\nsNlKUF1YnZ8zGKsuUHbga+lNmpMXFpJOArVaiWlUfG3t/mXFXexUwHs7xv7a\nTWmIUYxgu7YLnNrDK7fnv+0ji+chqJEPWya0Iau9iboOEMHnfDTyZ781o4Yb\nQWNXnGP+v1wa+RJpGD1/sz98spSPjHJ7JxSxF5115d3Zl2EBxAYm17J1uLUv\n3RAfyzVe6uiCoifFYSut3eKHF3kRNxi2foptQkNvrXNf8buIskGEcyVbb7f5\nW3gFvOwchyyJx5pk8kTc7L0f4tKQfcI7urFdye+LLTt5cP9Ru25J17XLOfDI\n0oGeZBHkzcHBtu5UqzAvxtgVY1gWt7lhmvMeHIAcUvZrRtl1NMagIsb88BOq\nRiisaMyMA9ZYg7GP924XIATfgvsfh7mwChPXFKRsQF0/O5BJM2++bXsugypx\ngXj2nJizfgS+Y5Zm7Tl4AGwF8FTY40kGseoKCbY1RUItEORD4OlyokTj50zn\nuULJbpoRj4aS7vjrJhw9qGlntRgTDMOfoVDwmWpkQzLzH5tQ7WnuPUufPogR\nCwat\r\n=wUGa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC3axL3AI7qM9JRK8mzs7WYlA/Cws2KMiKz6RpuKKlDIgIhAMVpyLyJX51dmQLPRYBQEMu4NLkta8ofeV9WoaCIqY1q"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.8_1532518946352_0.4156937909732612"},"_hasShrinkwrap":false},"0.14.0-beta.9":{"name":"hull","version":"0.14.0-beta.9","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.3.2","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"cc1b745f44519e32b17250298b009c8b3f151086","_id":"hull@0.14.0-beta.9","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-hzt8caSk+3MW++YGxi6HKNvqG0wsEd2rijJGDVXC2KW5VkGD8XH/jNCVKqK+0JJtiJ7kK17EHRn9UYiFzJlc1A==","shasum":"e158f11c2a9ec7299609c73657f047bbf9d5e0a2","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.9.tgz","fileCount":134,"unpackedSize":1120610,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWH/ACRA9TVsSAnZWagAACiYQAJIES/Qiuatua4dJFgBB\n1Mo80ZMXMj+uqTinBbxILY+ieuffIW+Ujmqjs83cQUZ3l02qLddrG9jIQ3Aa\nHOEIVsRMYO1YrHEymrYItCDKSYB1FwfkJbkDgkBf/CPHhJWsuEfnlVfM4RQL\ngEbsoMA9GRfKVRIgrAhJcS5Xa8j90EN+T0EnZATcvxRX/NfrdovL6wr8T/jI\nY660WbdGj3lekl7hG7dKwidT1MeWbmllnEXZKaxO0TDxHHjc/VubimgvSSWi\njwphy2mi21sGVGlTcwHR9iNhpVpjnh2Dr2PAqcoZaCCZ6Rfp6ky8r52bYYNv\nTBfacTCNUQQnGca7DS0rO/aygcPDNKm9iaAvw/qeedPpe5alt61TXSZxxoFL\nsf4g+2IpdjyRabSvzZDDPSMbrMCGdve4zc585XENZTH1N9uRMKSFki4D546T\nlQGbJzF/aUt0FEV/p8TGdfSVLSj50t5+oJClH2X7E7MDekvERnFk3vuKUVN6\n/Q+oxJ/SgW/12LB4ScCEBOXpkiDVDl/vDMcP39G4rHUxRKapb5OogujFGmEF\n9TrBNRCoidbecMX3ksfC/rg8czYiBuDfkFXFmGcPGfDvYpps3QqV2uxbBJpz\n7eHquYIRBJ8U405GP1pmOOv6dZnrkUmZYPEQlHewckCP5TL9/JS0l1uutPdm\nXFhy\r\n=IAOC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCShL5eSQc2reS6zuXpaz1EFGbOKV4n9XRHB3dPYM3xjwIgRt5nFuoUZXZLbyTzh1p2ENjm/LdiVH0pmxX3VItWGw0="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.9_1532526528051_0.955330503079487"},"_hasShrinkwrap":false},"0.14.0-beta.10":{"name":"hull","version":"0.14.0-beta.10","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.3.2","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"33c9df4ce78fd34e5348fa6186bbe260647df210","_id":"hull@0.14.0-beta.10","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-DzVh1CouueAS3xnm2m+/G/oEDNHrBQ3kFpYUWDUajRudnB1FCN777rGtY3fa99CjO3SK9fWxihA0d5I77RK8Nw==","shasum":"3acb406e24730d05a51da20e42779ed48c574681","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.10.tgz","fileCount":134,"unpackedSize":1121184,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWIJOCRA9TVsSAnZWagAAxusP/iDoxTb+z+wXkzzjYWhR\nbBbC4R151TPWxKTwmn0spIccQfeCqcRz3w0tkVAptdGlQK0o51YoDhVK3Olx\nlbEtz9xOw14SJCqGe1IlIK0dZpcYiAaLe3J5dKQVzMzu4zTgsgvdygYfhI6j\nw+usL9at1wDJL0CrHCPDNs8sF5k+s0x4I1L4Lon0t29fBnCgCj2Hc1U3+Bs8\nc+EkwxvKLE945M5SD7WEv+q7yhEj8kuFC2OzYcw8G5lmgCWlPoMBdrVQkJE7\nD7Ff5J2ieuwHHHpPS4z1jkvj9Dsb0zJ4yNjvoXhq4KqBi/Pl/fdprpSdyz2C\nbTzBD9adwCsCDKUqTvVnaS+rFOT8nwTLj4anlD05LlntFRYeFMRW6/EWY89d\nhVfb8+VXGCQanT2WovnNMBfvvYEERrj/EYCTmSbljZ1ufCRrh0jQyG1pL6sL\nGjnvRADdC/fwqB0LqtZh7btYSAeU8O42J2IyL7RPDeDxy6593jNDDO4HkIrM\nzG4CDWe2uYejYFdPn8R2ga8ouyCtwbKHwDpY8YXljKmfItwkZOPoiy/jzKnF\n80xeqC/Z0Ko4AB9l4hhfeq1GbZkHhugqrpRmpkKBZbt1dgsvHpYXVCE40ylB\nsST4V+Qip6fHWeN/0i9kA0VAynrIXUWdie1sxNJ8Nfswf/ytKkklm3IjsaYt\nYt4o\r\n=dRr8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDfs4VkiZpKYU4H/r+WfpAng7cAv73HROqH+Wkwa+NjcAIga9Wwcl7Ld5qxzxNiizlTGkU5j7QOVgQUGTX7S0yRYmM="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.10_1532527182001_0.7820736757967512"},"_hasShrinkwrap":false},"0.14.0-beta.11":{"name":"hull","version":"0.14.0-beta.11","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.3.2","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"33c9df4ce78fd34e5348fa6186bbe260647df210","_id":"hull@0.14.0-beta.11","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-fyeZ+e7HeU18i+F2IoDaUmrSTpTNrYSumY7bH2AZ2FQ28OaAo5ZQdOxY/Ery9yvTMiTG7bz9j1YK4IhMldXAZg==","shasum":"b9e304676f7fd3635e9cebc5b3febfe2bca6168b","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.11.tgz","fileCount":134,"unpackedSize":1121266,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWINbCRA9TVsSAnZWagAAduIP/jfO+iI6i2H7RVr2CQmF\nYCfuAYr2p5N9kR8x178vSxcN+33OXdBuCE3V3r/FQ6kdG9rDOZjvxauPHi+S\nSUh6xRSR7f5LT2hrAKBw0vlUFgSssNhCCA7qZDWj2VyyeJJsvC8OdsSDIjL2\ndkKavJICXwovQAu7+ZD8CcVEihnIY8aDSzTbaA70HUr+x4KlYsgZbGlDHlX9\n339tH6YGOUMbKy281kqc/z4BctCB3pzk6pvM+JE8JrsdrtU5mVR6Q4yyZN5s\n2eMqUL2FjB1aJGD8DmMl7PkJr5m6ZWx+JJnle8ZyhX+Bl/YnO/1dUPLVxzOz\n59J1DhhTsjCHKkYy3YV9M9vocbIkKrDtQJkSag4yzb1p6ZtoaeEcJOK5xFLa\npPB0G98g4EP7/FY62/lEDAXMCRyfjeibUEIyMQn0WB0Ql/ndmYItYAE6nW0M\n1aIe3yJL4NN3gicA2Vl41pDVrCUbnmVuAcdyHZsjgWcKve4a/ChEpAuY6CWt\nR2oFjXeCit+9liGdtU8CuI3PMRcWRSa9Ak74GB7AEaVKRkrBrNO5GR7h6m1G\n1sgPdxgJH5Lg3mHuiO16edwbt4yCk6xjtK5Ub9oIsTw8WHLdg5fDBZKqnhzM\nQAMyu37Zj3+OgEHJVTC6pzSiUE/kb5AykY7XxCQHZe8/fZbpjuOhm0R4xL0h\ngQS4\r\n=jakz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC43s4Iw2J19+DX36EV0WM7VI55KOYPfS+RWvoGHjYokAIhAJdrI5IkfwtRB14iL07U3GueVUH9+uRJBensAxbpcM+K"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.11_1532527450600_0.02345158498637523"},"_hasShrinkwrap":false},"0.14.0-beta.12":{"name":"hull","version":"0.14.0-beta.12","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","moment":"^2.22.2","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.3.2","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"5fecba5ff6d2742fe4394a88c335a5f67b453c16","_id":"hull@0.14.0-beta.12","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-T/QhNIhpNgnQnWvFUqnD3CjqijC4aBJr7b9BGgV5ixY49DasIms9uJ5YsguaF0zuFVTQHuvv4gcCe8tJl0BUcg==","shasum":"749d64b7f480b5fc9cc2ea7813f7d96c304caf10","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.12.tgz","fileCount":136,"unpackedSize":1127517,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWdnYCRA9TVsSAnZWagAAHW4QAJpWQnOuKU4DmGA5LvHs\nhdaaDo/sfLiC6H9jaM7m1e7JWNRhP0L6nwMCAX8/3xFNMwLodVFw0b1F8+tw\nFwym2QMHpT0Elf86Or+I8MDO7TDFcB+gQ+bG8VgWPeDtYJ+rxK1MvKHaTcGt\nYUUSMS36upH8cg3T2ES8bMSMxyGgkpeC5yhgGEcaSirsNFJp7PRoquNz9nYW\nu6Jtn6gvSLXVwKYg6iNnl4BPd9iSp8Z5HcT9YWiX5jRgIz6Z/CnlF469oK64\npMLq2xCTbAdUQOON32LmOaBmDjSpIDgJXnWQovf6OHM7eGIjWWAdEGkx5S0i\nUf/QGaxrW1KH/ZQJmOgVPeXw0D/FzTbwOb8XKihB3Gqv+cz7VxeJryzbmSbW\nvB14vpr0TmlFlIQFZb3ExGdDSsVMrt3mVPtlO40oTP76Apl6C38MJgavGu5p\n2FxEPURDp+zAgAohRjmnrwPG9k/q/ENZS90xZgwSp6WHVLImaZia8y4//eZc\nMUBhv/xnOKuMFMDuN06hMV2sxWLFhCtDYFuykrpthK+nRAxLwxQfvxS17R58\n8XWuJ71C5+TdU8K02UslF+AifWsRjTyk92G5EV7u/ja07b6ppj6bessB0tVN\nU6+eIkdR+p7YPX7NGu5vM34d3p3UaqdppLZ713isISVk0YX1jD/r012DUugJ\n1gBI\r\n=hD7c\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDAKmwFEMjU2Z8KlOVc4BJyMqcYowlPDFNIu9iVLL/vRQIgUcXDYImla5osF5XgcWoCoWVP2dI1tjBWG8u0VNCLv2c="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.12_1532615128850_0.6928803000706498"},"_hasShrinkwrap":false},"0.14.0-beta.13":{"name":"hull","version":"0.14.0-beta.13","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","moment":"^2.22.2","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.3.2","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^3.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"0fbffe42dbd91918980987fe29f8ceaede01d720","_id":"hull@0.14.0-beta.13","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-R5Ky8nbnmMiCIM5sT66MHDhSx1zIsrt4gLt+uN1GeiTFLtYVUlGQy6Ta0B3eQlx2kZByEVd01plBTMvTJjFO9w==","shasum":"6faf37968c4e8f9352dd15b84c99418b2456b0fe","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.13.tgz","fileCount":137,"unpackedSize":1130991,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWx2rCRA9TVsSAnZWagAALwsP/2Ao9YUlra+5B+xPg7uu\nwx/fkVvxewaZEtg+NkZ9oBs6mvEDeaPuol4oWzCFGex1lyLRGsgDvfVeP019\nkYOqoGzBhP+OVjY/lEEKAx8nHBz632jFtdMFQnWjpgaKGAq8o/d/I8Ovi+eh\nq0g6jsdFIaGCJOkAubF4c3KM8zH1M3GdoKaiTPggodwEaKa2okibz7Lb/7Pm\nlxi8P4yINK3nILyVwEetUFMf2BDpKEV7TPRrYI+cuTpEpXEGd6xae3NAHvTE\nY+xMGt58Ru1jK0VumRQd6J6iYXVWr0LW5AwEhfSg9iuZ8VKciZ3IlB2ifW+1\n+DxKZo5G5YUGDYxYWhwYh8uKC8lYCfyO9yG6qx+5uRgQ+3yY0GNfOaW7AMNj\n2KEO0FnCmvrdYCNM+IHuth4ED+/Ha08O8sdNozp1xu99kTq86MjPoR6vGJHq\nK9qbfeOoSVxiSngJDW5Ax9vCZVMk0tS1hFdrQ0ucmG1yoIVd8sL6Kq8tNrEK\nibs4+kIjC7dHk1U9TeyffJbD9GB/03BVCZDY+JaPoHx2tkPcT6Y96857dYiI\ngrGkGixpI7z2DxdxoFEeNUno/DnrSUDZzZ09Yw/3PZzdlYjfte3AjAFdF7Bv\nt3LvTYFV104mF1vbReuDiaEikuUGT3BAkhJ5QXQ7zV5ouXht1/N7zaYjkcn4\nQFTr\r\n=lKHh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRxnCwIjd73DAPVZ6/llnVlHVS38e8cAWZChvKgCNS6wIhAJv2M04vnH7Lt3MXkejCZ/pB2Y/JOb9dwtTjnRGplHW1"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.13_1532698027548_0.29405155930830507"},"_hasShrinkwrap":false},"0.14.0-beta.14":{"name":"hull","version":"0.14.0-beta.14","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","debug":"^3.1.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^2.0.0","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","moment":"^2.22.2","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","uuid":"^3.3.2","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.74.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^3.0.0","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n\n### **notification**\n\nOptional - if in the context of kraken notification, scheduler call or batch call it will contain `req.body`\n\n```javascript\n{\n  connector: {}\n  notification_id: \"\"\n  messages: []\n}\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"070a1eabac6ba66afb2b79c2570fafd71ecd9ad6","_id":"hull@0.14.0-beta.14","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-QrQaXCAf+Hn6NIcuFM3AxXbZaMAJudwu8+//XgRwCjlsEZgk/xKzgm/Eqw0XODXWqpWrNDj6A7fRevLOHedGTw==","shasum":"52e3f027d960fdc8a3d4bf46a661d27b613c7a9b","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.14.tgz","fileCount":139,"unpackedSize":7610839,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjOSmCRA9TVsSAnZWagAAJ6sP/Rxogv498wN5IUf5jS6R\nGjOIEKHtID2eIPWlr+HUHkXeTuRnS9ODIE+ZK9m9JgrHbf/rp3yCE5DUrE65\nCLN57+5qs2z8TSS/4/XKh8gzhoqHC7zls8fvRuGXoZC/tteJ10NSrpJUSzHC\nHdreKrp4M9nAHIDXMYReT/1yQLwpCk1bU/OT1hYgncqijR/NLC2PA5BbWV0w\nEqcuQ0Kygpal7Bc8K//94d4BvaMokPShJ9PFz+qr4cPZrmrRiDUMst4AGtVK\nPpuqPUS8q1Rj2XFcW7nUvQa5G72BteC9F+2nDFS1b+Px5p4XfMeo592xMstr\n5cfKEYBx8IhkjOTsjm8YQpOQKSD4DM4jmC6axR63rC0JMcjOE3KYXVGoHh9E\nSULkMmG9QxnR9rZqsyLgd/v4FlLaTIoy2/ySNkUGfE9+Yrb8ET5GCYlRa8WG\nItnHWpPiMni1Ep+7BRkrIDuE4ZNq3b9tpFDqt77R1L4Y312RjGlcEGAmN35Q\nmsqKKvJ+r0CNi9xVj3HvRsklZO/ep6SnMl0r2iijzCuFazRMhI6cqwbRPkUU\nDljOlwnkmVrXu2aC90V3k2TIRi7m1jiX4wWB8BA8IQ/1Z53XCMsAR/ZB+ebY\nd9NY8iGBV3GVwUiWgKh+Svzmsu2TGLqMDbedmvFlbbVHm5RlxuXV/GFwESxt\neSkT\r\n=cOti\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKM8/erQ+CM/2/CWgAAcbk8JOuQdNfA7g96VVL463qlQIhALnzJ2XsG1/eLptMaL6/Mu+EH24gQ0fizcrPf6ftOlbA"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.14_1535960229485_0.9728402910616218"},"_hasShrinkwrap":false},"0.13.17":{"name":"hull","version":"0.13.17","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"^1.2.2","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"397f2db5e8dc5227fa2ffac517a483789594a5b5","_id":"hull@0.13.17","_npmVersion":"6.4.1","_nodeVersion":"8.12.0","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-nZGI65CanQOEIljUq2MxIi9ToR6LWALjEkqh0D09vhvzNJH2kwumYIRvXg3m0dQnpAGIiZQIupRXewtNMekpXw==","shasum":"01efd2af682918ae0717ba3497df31319619f3e9","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.17.tgz","fileCount":134,"unpackedSize":1087699,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcAU4VCRA9TVsSAnZWagAAuooP/2o1W6goP0VcOmhwFlYK\nMkkgCUIZ/UjfIQqqjyFETq7RuX0MpB9IWiCYKwGYZjklNdcZLHlxAxVMXXph\nBywsyayNq8ZbAgIWJPe0hqucSxmNeylRJWmBZoj1V2omhtShl1ZcoXrkCiRJ\nlh7YZoNtoEXYrxCg3nKNUQAfnV5rnWVgs0A0+x7EBG0TC+GlpQjGSz6isHC6\nWa1lASD8/WKG8j+1CeOfaoQ8aj7BaCsf2Pnmth8Itf/A5UsVl1alV8lFeW1B\nEr6YAfsVrK12DJFxuTcFXDFR/Oh799Qu9Rcbr9InuYlD2LKkEui1I0iTym7C\nYN9RyKGbVDeyGsycHzvrNwAnNCaAMaEEeHiCPweJQZ3QcDqBdxCRClAdKI2Z\nK2dZP+CTYYarDrKGHjcbalBexJoiBjFh7h5CJKoGq202q0oIgXWfKmVx3uUG\nCObxlE47IylALXDG/TAHkj7bPthOFadl5gXitoFjTrBxryRZYO2ispXDs2xO\nIr13G3Vyz3KwkuJ7iWm2w4a4RCcrBOZQMOQ8gS8O/2Y2f7Ve4fl66HEnpgHI\nyCdD8IDd51OUPEwVkQmL5BTA9XnufTdZ3bsktRE3eJmWHBUh/e1tP+P+cT4P\njhzB1N3QhzBuS6VG47MefP0NkLlfDbYeu3no+FD2p3BzI9XwqakWcHrlFzUo\nN/yf\r\n=PgsX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAI015VHyVR+pc9Kg2SmD+6osTQyxyvSc9TrunjvUFE3AiEA9Aw6Kc9cXHTUx1BvrZBNq+aOyHFAruLZlvjy1eK/qNE="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.17_1543589396451_0.6080152393318925"},"_hasShrinkwrap":false},"0.14.0-beta.1.1":{"name":"hull","version":"0.14.0-beta.1.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"2.0.0-beta.1","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^2.4.1","passport":"^0.3.2","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7"},"peerDependencies":{"express":"^4.16.2","newrelic":"^2.4.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.71.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\nExample usage:\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options, refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# The Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide a Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: {} // Instance of \"new Hull.Client()\"\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  message: {},\n  notification: {}\n  smartNotifierResponse: {} //smartNotifierResponse object\n}\n```\n\n## Base Context - set by `Hull.Middleware`\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **user_segments** and **account_segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` (aliased to `segments`, deprecated) and `accounts_segments` expose a list of all the segments defined in Hull for both Users and Accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n### **message**\n\nOptional - set if there is a sns message incoming.\n\nIt contains the raw, message object - should not be used directly by the connector, `req.hull.notification` is added for that purpose.\n\n```javascript\nType: \"Notification\",\nSubject: \"user_report:update\",\nMessage: \"{\\\"user\\\":{}}\"\n```\n\n### **notification**\n\nOptional - if the incoming message type if `Notification`, then the messaged is parsed and set to notification.\n\n```javascript\nsubject: \"user_report:update\",\ntimestamp: new Date(message.Timestamp),\npaload: { user: {} }\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior \n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n","readmeFilename":"README.md","gitHead":"d1674f405a675382c3261141a7044ff6645f9ec7","_id":"hull@0.14.0-beta.1.1","_npmVersion":"6.4.1","_nodeVersion":"8.14.1","_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"dist":{"integrity":"sha512-DuZHu7gNSfh3QAeflhCkoKrjxeAdA9uXCI+4LwIYHbn4OsCXIJMw54VJsVe16A6J5YMCvqhVHjmuDGYbC1MgHw==","shasum":"b65659509de6712bbfe053e8ea29b0cda3590036","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.1.1.tgz","fileCount":100,"unpackedSize":562522,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGr6yCRA9TVsSAnZWagAA9GkP/2PHvPs1LASk6UTugDIJ\n9yM3QajN4oPwOzMW7RExhWAgvBc61hDHfmHkmH2wsogjt7w9z3dNdkaUiIQS\nC6vmmaKmLPMF1xwvGaPg8QH39gyYZKcCp8hFlhOylXXkIyaQ7wJlUjyloqMS\ne0JzvT+39vmg2CX6njY7/Zg/CnTY0MqNkurtoAtSotXbOCu8iZUnTsArYOJv\n4+cuuKgO2k3fx6QbFCtOYMVeBX58Ak7rYL6MzHyXCeIkhoSZYg3S/RMKFLMR\nfDSbfn81byoMOb4B296AHYWY7ndrymX3Ctn5PqurUgy+4YL9ZlHL/DQpPtgj\nMTLzbVhUFPj893ta0aKEzWGh9fzrFJbsUvzuF2w0Az21of5FWSPcIe+4isjV\nUxYOI/9ciOZg6JhTac7JRTs9AwQttb8jt08rMVViDSmQwpSADc4oYfvnU+5r\n4dy+6FkFM7p6EVhAc0fnmpOPnkGyYhXJMgZUIjz7sK6phkV2PnjZVy082X6q\n+jx5l2eKp1pajfqCQhGZaCD5DyD/VDPOGThxtP0Cue2ufQQU3ssmHk5cpLgp\nmYIiVtilpr725RzHaX4/Bhg1Uy1JeXCX/5lkGtC8X5TmbDSsR9361NkffiuE\n1m4pMxYnZxztUCyKLEUYBQxdzSbwQRe19HZ2vahs4dpqyojFbqguY4M5aQsa\nfaZA\r\n=3P/T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB+qyy9vyK38YWzUOsed4IUPJs3XpuTYHD42lcWg48TZAiEA9zkp3hQkN02oy0zFG9MUN0HQFE95zSW0xeUgJilnVmI="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.1.1_1545256625749_0.05080523051092345"},"_hasShrinkwrap":false},"0.13.18":{"name":"hull","version":"0.13.18","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.2.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"8559abc0a6d618a13a70b07f65712d7fe3113cd7","_id":"hull@0.13.18","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"dist":{"integrity":"sha512-5fzIGGSdzkf66x0i9Nw6kEI442LaNDzb/zARdDQKwnkwvR5IDq70/FcV+M2zhyWnOdLou+1+rPvZ6SNlsyE3JA==","shasum":"08e9d61657910b0924ade11aba0e1ae23ee6fad2","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.18.tgz","fileCount":124,"unpackedSize":967080,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJci2dICRA9TVsSAnZWagAAU5cP/iC/RLf8zz+DEO79xd3T\nn7jl9KchCe/s8+NDcipvM2tsyl7lHdYDJj5K9fX2LaZGP+LCBtOz1JZNqZfJ\nTQ1vRtswnWVMVA0QZY9xJK5hbDMxQIyfLexRuxLxCr614W1dH4KMFcWhusSE\n38/Xr6IMIekpk/8f1aMt+cppm04uY2VovU5xWRSSZBvKEaCcrNLVp5PJpHee\n56rPFrlraCNe7fsywjr8DjofHXfID6Ciclb/I8hM95WLDovdAoy5IRxm4Iuz\nFAX8AHVBsdNPvDYhav9zhGfrJ+TJCKMeIWD2y5mlaJiV1Gp3VbpEcncYt1hb\nFuq0MtSKkVD1Lnncb7EeWJLaUPlW9tZdXWkAP82gIOzzcma5bfygGEc1jGto\nceRWzVf4EDrCCHSYHYNa4uvy+1ILQH1BLfILW0IflKTHoiGFMTQquuK2qe5j\nXgiv3SKxN7Z8sstNfbIuLM0/SHvHU/Z8jYLW3M1Oyh0ukST+yGgMaTRtTySC\nSWPzkRWpKvqAG3B6OrnTJSkZz5AdxBOlYNUDxfIWxP/faQSG2ExYZhO0cYv5\n9ICB/JL2y9GF8U+x/46hE08KD7GCIYpGwjK2a5Cpx2L6MyqZOUUtip7YQMx3\nKYa+FuZUgpJgCBKx2vXBNw0x4Z14CE3ENZs2I5Y1jp3rEJI6Lxv9og1rQOmA\nRp20\r\n=afOt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDeOUwLEEtl6YQdAnxtrXOvnIJq84LrKzJRy4YHc/XJPgIhAJRV28wxx5uciUy68T6yJUVXuuYbjaKRaXTAjcX7Lkf/"}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.18_1552639815579_0.12240859322247344"},"_hasShrinkwrap":false},"0.13.19-beta":{"name":"hull","version":"0.13.19-beta","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.2.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"169c70b089e43345b11c418ea0aff3d987dd57fc","readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\n> Example usage\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: { // Instance of \"new Hull.Client()\"\n    logger: {},\n  },\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  message: {},\n  notification: {}\n  smartNotifierResponse: {}\n}\n```\n\n## Base Context - set by Hull.Middleware\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` param is alias to `segments` and `accounts_segments` exposes list of segments for accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n### **message**\n\nOptional - set if there is a sns message incoming.\n\nIt contains the raw, message object - should not be used directly by the connector, `req.hull.notification` is added for that purpose.\n\n```javascript\nType: \"Notification\",\nSubject: \"user_report:update\",\nMessage: \"{\\\"user\\\":{}}\"\n```\n\n### **notification**\n\nOptional - if the incoming message type if `Notification`, then the messaged is parsed and set to notification.\n\n```javascript\nsubject: \"user_report:update\",\ntimestamp: new Date(message.Timestamp),\npaload: { user: {} }\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier-handler\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier-handler\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier-handler\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n\n","readmeFilename":"README.md","_id":"hull@0.13.19-beta","_nodeVersion":"11.14.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-9vhOoKBSiIXcXhfQa+SSwglnz4k/BQq+QJwf8RRsRv9sWbXysD5Gi+wOJb+3H+IPeRce4ZK8y+6D6z6tD51RNA==","shasum":"24e59a70e12dc58225621238977b1435fda6744a","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.19-beta.tgz","fileCount":121,"unpackedSize":567919,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc9oS/CRA9TVsSAnZWagAAhmQQAIoVMvWsZbGjiQZZ3bhi\nZ7wFC6cAacO47Tt0HC5tP3uZbS2Ec7tqFkuxi6pL0Fsm41rBx74qMa6Qu9EH\ndV5vtN2kNR8Ki0ZZp7/0aIiptd5jtdKEQv915JjixhZ70BZT5UGBp6DUXVU0\nCHKQU4cs7bHZXYywlxo/Ns3ToLISHzRUMqkaG+jtWgyZsRYmZ+BZRuEKtZLv\nGIpV91e7+spwQXz0um5LRYupryQdrZo7b4p69Te+2hKzFxBKbSGFVPUBPz8S\nJE60bsPcLwaIWm/RNKz+y8MjDNn2PG4sMY14tgDzDZSJsZT7EcTFnvabo8bS\ny1GuGrF1Itxb/KtsPV/ogt/mLu4ItlAEimrNBgWzmdK2LM7j7K1uw1eZIjUz\n/g558fgf4UgaLsg8GMlYknO0LdTFrUMVcacPYI0HswfN3bCIxdxYqAnrG5vw\nffwyxw9zE4qXA662gv5Lo8nOex9pYLENvOUFoYrT+Ui7dnVeg8cuJnq6GjAa\nmPyZL4iSy9WHqhZ2ZbVXvMPBu15D+BceG7rBZflWT712vnLNwkAaqlpUSVqx\nevzYCL5sy8Ixm0uB8CaQdiG3dL3OuoIbRHjG58LGCr07xySXJhYOPLDhNL7/\n6qlHLVy0QC28NdWuxBZJLOKwK50do9PpgKeuzUQXgHinEfG0WNzkf1fcQ8SR\nMzT9\r\n=aOFM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDPhLdSkuZ97Ntz17V472K+rQkYcKDKbNLezw6OKfpAXAiADEDlq8ylnMdHtbMQm1uV2eg+Y/OcdGo3hZL1L2u2Pmg=="}]},"maintainers":[{"name":"sbellity","email":"sbellity@gmail.com"}],"_npmUser":{"name":"michaloo","email":"michal@raczka.me"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.19-beta_1559659710817_0.13745453126433138"},"_hasShrinkwrap":false},"0.13.19":{"name":"hull","version":"0.13.19","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.2.3","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"2a9371da0ff9602fdb6645e74bca39b3cbf56cf9","_id":"hull@0.13.19","_npmVersion":"6.4.1","_nodeVersion":"8.12.0","_npmUser":{"name":"timliuhull","email":"tim@hull.io"},"dist":{"integrity":"sha512-I0+sCCXaf+yLZCrSU0jYUPLrtvxQroU4qCirPj2Ve3/Q7aFfF8N/uOpG6i6jd/Xdv+ikSGdq7wWAqKUzGLqLYQ==","shasum":"3b5bb1f06894475c43d4f17f4da20b8b61629046","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.19.tgz","fileCount":122,"unpackedSize":979918,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdY+S3CRA9TVsSAnZWagAARQwP/iYbU2LcYtaBu9XOCEVq\n85AYjgpD9DYwfoVQHt0TWPhPrYQ+zHZU8uT58RWr9qMIDnP/nK21RvixTUA+\nT/bD25yfBjtqxg4pUigGNNMEffIZ6CAZq7J/VhwwQ02rI3Mmb2GNQ8v3ktyK\ntXmLs35p5900lE4VvgLnliqEumkIfkbcDMnu2J3aPX1ZfEPWqhg9n3cRsidq\nf2nQV6zya8KF0Gjc4+R24ALCocHsDv6B2waT8QfgRFpt55zPWtkmQ3qJ3dXQ\nFwye+bjcSYQJAFbltdheOFSKvNkMCxfYlbrmlQyeif131KCAgXG5i0K9zw9C\nEZMdn1AmSMqjZVM7UTUFsSjbfs5g0jOjZvjGPyq2xowC/N8VdIL/G/hJE5qT\nKLNbbu2IrLbsJR0bFfRXiESNeBdEhqsmX4PGUnFTBfa0qvypYtsCksfqKFAb\nFrXAJyUkQCjq7EOU3ou6Q+TtXeopNV5/cwB3Lq7NGxgjpu+dQQVpY8lTDPO4\nh/8VpePkzocX0OaOEWfHY9qf6zJHuTYbPo9xnNLGSNJS2OVjERsvtuNUAvBM\nFeg/2lVirYYyLZ/QMwa/1aXMII76vuzHHlLVRL34jCPLz73Vqjj49/rF1GdB\njpkfMaCzP4wJ3or7rJJcFUjLkoFxmwhTlcLrQJ6OyXV1bLo1nuWpIA5wx6Gz\nB4c2\r\n=KNYA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFsJL7ULmueHJmI62hnAwjZJwXkceHdTnwQxzDG5yNYXAiB0mWjeCRC7ahl3YxcgPKLpnYR1cz5aFcRgp9fbDsD1pw=="}]},"maintainers":[{"email":"michal@raczka.me","name":"michaloo"},{"email":"romain@hull.io","name":"rdardour"},{"email":"sbellity@gmail.com","name":"sbellity"},{"email":"tim@hull.io","name":"timliuhull"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.19_1566827702174_0.22154424772512615"},"_hasShrinkwrap":false},"0.13.20":{"name":"hull","version":"0.13.20","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.2.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"0e022029e57fdda615ae1527fb3ec247f83f427c","_id":"hull@0.13.20","_nodeVersion":"8.15.1","_npmVersion":"6.10.3","dist":{"integrity":"sha512-mfbLE1kkRkD+i6t6tH0SOmn/d8dBYCz72NMIg2lxcgr5sXK5BpEOg0UCn1l0yedAxrf2P2mmBWTe9GsBwf4Hyw==","shasum":"0b602cc809ba519255060430060e424fdd50a5f7","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.20.tgz","fileCount":87,"unpackedSize":395172,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdgesVCRA9TVsSAnZWagAACN0P/2MWC4W+ZTZBeake+R01\nRryF5I9FbEyg0DOX0aKdmoplMxV66ZQ9+AG+SqnmAoWYpAJFDRWLGrTnp7Tl\nOBd+mGe4BzHfxe8Zw4aepJj88jiMSF21l9M3d5IkBBVxk5mB36DyBxz24MWs\n6bEoU3C2PSf3MqscGCftKiCPg3PJr5MgHLEpXTlW3V0kB3lzpMf3v+VyZGNG\nT2KrumNKdaopPyy3p9IcsDfjh22SE9fx/gpLN3WJrzLDOVl0Mduj3ytNLAP2\nZ61ZKvaByObYIOIA4sYx344anWTKLd4IldD7bgA8FTRmN4I+blJrRquTvO2E\n5fJjisPPSd1a6ZxZjVJfyJojRJApq7fKp/3Qu5aWlM6MBsBpEm/2gobhG4/a\nYigNgQMu+o67TVzGEvjMoYBzhWddAVKgqqRjI+inoDMMGgnZUUbFTlpyFYxQ\nRP9FAZqgsJaprjNbf+i6sNlJiRGVuMUnRKJQqxZgM88Tc8kP/tmFF9ALxxxf\nWh2p4x5s3hJzZ8sEzpbOnsYhYoh/NjeWiDtYbSe1DvfJvW0o4rJf4oCgZFsM\noHl/kQoIwOmMWSS1IXJGAzRBzPSfYL2EGNbN4qyhya7FQ8kQjk0km0IF/ipa\nGS1p2WdZLShbNQpNZMqWAPjFyMJPMEw6tyIFTH0w32VyHByq4zrIHfyjSbeJ\nL4GD\r\n=SDQe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDfjcSXv9P9BUuGhu8sgHsmHs4YlKyl3VlToaVkeML5oQIhANeJGw7s7iUKtq+7h7oscoOFDZ9k/gUegUhkqj5SqbkX"}]},"maintainers":[{"email":"michal@raczka.me","name":"michaloo"},{"email":"romain@hull.io","name":"rdardour"},{"email":"sbellity@gmail.com","name":"sbellity"},{"email":"tim@hull.io","name":"timliuhull"}],"_npmUser":{"name":"sbellity","email":"sbellity@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.20_1568795412901_0.16925695046371558"},"_hasShrinkwrap":false},"0.13.21":{"name":"hull","version":"0.13.21","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.2.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"d5945dd8889523a3a2e7f880ad65ab507528c6cf","_id":"hull@0.13.21","_nodeVersion":"8.16.2","_npmVersion":"6.13.6","dist":{"integrity":"sha512-gV4zcueTJHrYwKfSvh+BWxa2FCBhse9AwKe+kthdUkwcz5gVvDcDRAtiQDa1g19E09qxh1LcVuk8quGUM+HCCQ==","shasum":"5609aa3ca22dae1fc02f443f990dd4aeb8c1f528","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.21.tgz","fileCount":87,"unpackedSize":395940,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJexqVgCRA9TVsSAnZWagAA8pIP/j4YmweeDwWcA+wsydCz\n2XE2OnC2Z3iflXOIcJ/vMpGWue2T513yDBzjsZCPs/6FyE/vv/2MNibBXJPz\nesAsH8veKIRCzWPQsWRcszRWv9o7vC5MKdqb1bLGx7LTw0YXhv5Iucs+zn+/\nt7pOQWx+xIbgA0Vn/IvuGDrMQN1MO49IfYddt4eCO8/+4EiPHFh+86K6ibXe\niqBFAfl1cOeiT5mz0fR0Qlso3n7rRa+SXcmnsb0Q1iOPOEbPWc8JLrgGlZn9\nCVsPXP3tubSU2s8wn+ixoW+EK0pQ+pfDI1xmyQYDIkc18HXBrR3K6j8QVigl\nQZStaqoUWkDVZZDwa9dhQ0epljUSbzHgYPyAisLfhP3bTdjc4sZeainJrsPn\nVjz+zfSW3uQBkmVpoLMYTbsfmlk+JhpfNblxYMHIatwpzO9XS/IKrXkB0sRl\nwC58oRHnTp6OHqTT8UVuTiA5dea/94lQSKVDpDKx3YK6sFxSScUdsr6YD+/u\nKrd759/L0Me7XpW+pMG4aQtZAIgZrJsz8S/i38qbKpBX/Si8fRxn7qXq1a+o\nXtKBrnmIwkVpVkwqqn4cdGkEpSr3ohjeZ6OlL09UqhhaiyKszPSM6HbGc7FA\nSAogmor8/UEJzRWSANrhCSU2Ab8e+2VUgw3NDJKsxTEb0JmvlaUnAlQKg3cZ\nXeP4\r\n=Lkhx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDWNGtJqM6alcBh+lzZWsFXNXDCi11v0/ZYOcCJ+LVv+AiARVXjWsRT4M27rIjYAD78I7E2ssg+2SVeFXUQmW5Oh1w=="}]},"maintainers":[{"email":"michal@raczka.me","name":"michaloo"},{"email":"romain@hull.io","name":"rdardour"},{"email":"sbellity@gmail.com","name":"sbellity"},{"email":"tim@hull.io","name":"timliuhull"}],"_npmUser":{"name":"andyh565","email":"andy@hull.io"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.21_1590076768408_0.2740040439976086"},"_hasShrinkwrap":false},"0.13.22":{"name":"hull","version":"0.13.22","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=6.12.0"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.2.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","kue-ui":"^0.1.0","lodash":"^4.17.5","newrelic":"^4.1.4","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^4.1.4"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"a71ce27069e009da71eb0d7d6f870c4f0f7bfdf8","_id":"hull@0.13.22","_nodeVersion":"8.16.2","_npmVersion":"6.13.6","dist":{"integrity":"sha512-b1ufTmmL0meSzWva4FAgi3kuY9UpNGDyz+0j0+XtWC8vMAS7n/e2FhW8DqXfg553Dzzbyo0K9q/cqk2cwYlG5Q==","shasum":"d377ca33fb6985b20615a19333ffff96b146e3d4","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.22.tgz","fileCount":87,"unpackedSize":395979,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUWyWCRA9TVsSAnZWagAAuWQP/AnbZ0e/1IVJZdI0nsWa\nQLvgPGfjRJCTYOKpfMTYsMXFB1jrTA3yQt4zjqQzM2TjxTJxKOIMbRkcNSL5\n7LzJlURvL/k3565eoWWdsoD1ZKz88T5K2NtBiK9fkS7C2S2y+qmYjMQkgjcJ\nKuGkuPArqinFCETZrEIi3m/BSB9xLzwK3PhLaaAf7ZvTuFnCUm5I3lllrpKB\n89ZtNqoXZrLu18n8Rw9iidwkeYad6ia82mjmGXqwQTtPMZ+NVhQljL+U2RWH\n70mLv7uJZBQDxhRuxtkEHlZ+sfIut2iKcdTi1g5//7BueFmsuyZP4QzUMlwi\n0TW8xzPseOc+iz5TLB+nS6PbYXb/n1IUoc26RAPvcM05cV8UflnfWuprpjw9\ntiEkTnOPQOUe0dey9lisibVlwJGTDMe4eRAEQRhTkyrJvR1jROtba0Y0YG6l\n5YhVY11BRIOQA//Ig4tcEDiJ8NpjyjUqHGpvbkSXxufOWjaAHkVHVa68IPGA\n/o3j9xvtxlbivN0M/dKUNoAVZX/YNX5C5Gtx4mBTaFWP034gDZPhbw7nnsHx\nowITOWkH+rdVhKHYv3HF7Hddp6pO69ehVYzuEqLpsEl9HHfYs8j/hcu+I9tp\nIxMWmtl/Zksari49bwsSQZyc3oA4661kqe4cexzsArUJuvSAfxvQLrHGARIj\nsXEa\r\n=Z184\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAkYCzEM9TBMSlOdKylt9QNm0LC1GmHcw4NSdzUBhfTeAiEAjei37t6UnOYQ/DhMRfk/ke+8aaR7rJZgruJEEfXh2xU="}]},"maintainers":[{"email":"michal@raczka.me","name":"michaloo"},{"email":"romain@hull.io","name":"rdardour"},{"email":"sbellity@gmail.com","name":"sbellity"},{"email":"tim@hull.io","name":"timliuhull"}],"_npmUser":{"name":"andyh565","email":"andy@hull.io"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.22_1599171733836_0.31344266219187755"},"_hasShrinkwrap":false},"0.13.23":{"name":"hull","version":"0.13.23","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":"^10.x"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.2.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","lodash":"^4.17.5","newrelic":"^7.1.1","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^7.1.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"939266c7542fde0193155ebf30923e80ce9375c6","_id":"hull@0.13.23","_nodeVersion":"10.16.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-QhR/ZwUVB7jmbKx+kLBFv/01TMhCCTmAvtupwteWXeFbnmDisZ4IAyBF5IJktz0BkH8qFcuX1tf6giAA89d3qA==","shasum":"fd8b2bb8a5867e7a5fa73b0222c9d687dcf58018","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.23.tgz","fileCount":91,"unpackedSize":401807,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNBrpCRA9TVsSAnZWagAAekIP+QC14V4V8bcVMSWC8RU4\npo+5ptmuSBgA3vM7doxpZBaIG2WAIkDkjTnXD0ZGbhOslkfNf0lLmDU3XHsJ\nRS89XRMNbXiLdcYUEkJ0dJXHlcu1zV6CiHT4UMLSwPuxuKhVXEtYW95PGIYJ\nXe3leZBDwvn/wcxOG0fDtAZDqGO78KrzjK3DqVcPt1N+dtUB5eQamLE7m9bw\nM8g1XWlLGx67AyLW+wxdsn4HEpqwCT4VhaTd0+v0q8Eh9skSubZrMaOhGMt4\nN2CJmpGURn1BDUkrccdp7daW3VJ9yYfmNdBr7h2VybwuVTR+asESm4h6rsMG\nEfKppleO60Ow1o7S7fisuU+G+LFENxVY1ZkxVE/goSnWVRSlHAopDgT2Ceyd\nUBJwIN3vYDOj9x7Eiq0Dbf8dIyypXQxHg0Fa95Je/dyP8h/gP0xEe3O57EoF\nYgckQWhDQFyET873d7p94htpH9P27ayatnKrcBn5L5ViCt/iNB/zyaqdOnfX\n0K5IN93CGmw7f3TP/FHzcjW5qj2Ir+SHu8cbj3Fjg27v6SbmYThI86gSFqIL\nhPYtMr5LJbmt/vYySw1FFX8cdZ5mYVYf93STmX/3prfwTeZg564EvhMkuY9O\n/uzyPZnEQAuHtGDKM+z+dqeRxiCstzOvfhWy1Z1Xrn5Q3eivyHd44i/CYdEy\nt1HI\r\n=JVNf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB+INEmKeUSjId8T/WN6hjvffjPt7EBb/UFCO+Yl3d/AAiEAqGUrUbbn5yFkBDjKE1jd6ee7IPd4kbjeUqkXdn3htmg="}]},"_npmUser":{"name":"rdardour","email":"romain@hull.io"},"directories":{},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"michaloo","email":"michal@raczka.me"},{"name":"timliuhull","email":"tim@hull.io"},{"name":"andyh565","email":"andy@hull.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.23_1614027497253_0.3785289020017035"},"_hasShrinkwrap":false},"0.14.0-beta.15":{"name":"hull","version":"0.14.0-beta.15","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=8.x"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.3.5","aws-sdk":"^2.849.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.7.2","body-parser":"^1.19.0","bull":"^3.20.1","cache-manager":"2.11.1","connect":"^3.7.0","connect-timeout":"^1.9.0","csv-stream":"^0.2.0","datadog-metrics":"^0.9.1","debug":"^3.2.7","del":"^2.2.2","dogapi":"^2.8.3","ejs":"^2.7.4","hull-client":"^2.0.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.6","kue":"^0.11.6","kue-ui":"^0.1.0","lodash":"^4.17.21","moment":"^2.29.1","newrelic":"^7.1.1","passport":"^0.4.1","progress-bar-webpack-plugin":"^1.12.1","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.6.4","raw-body":"^2.4.1","react-hot-loader":"^4.13.0","request":"^2.72.0","sns-validator":"^0.3.4","sqs-consumer":"^3.8.0","supply":"0.0.4","urijs":"^1.19.6","uuid":"^3.4.0","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.25.0"},"peerDependencies":{"express":"^4.17.1","newrelic":"^4.13.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.26.0","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.26.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.19.0","eslint-config-airbnb-base":"^11.3.2","eslint-plugin-flowtype":"^2.50.3","eslint-plugin-import":"^2.22.1","flow-bin":"^0.145.0","flow-typed":"^2.6.2","husky":"^0.14.3","isparta":"^4.1.1","minihull":"^3.0.2","mkdirp":"^0.5.5","mocha":"^3.5.3","nock":"^9.6.1","node-mocks-http":"^1.10.1","nyc":"^11.9.0","rimraf":"^2.7.1","sinon":"^2.4.1","sinon-chai":"^2.14.0","superagent":"^3.8.3","supertest":"^3.4.2","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"87f59be6469b931966726f727704781ed2ee18fb","_id":"hull@0.14.0-beta.15","_nodeVersion":"12.13.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-36kuax82RlwqRe1M7mPhcNo08c44vM2D3OfQm6w1+sFwrAKOYmtCJmGaziG1mtLp6DPmYB/ZUlhqrbe7F5gmUA==","shasum":"ae19bedaea0c067748b0366778982191cba2e7e7","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.15.tgz","fileCount":112,"unpackedSize":589790,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNN8+CRA9TVsSAnZWagAAIXoQAJNXes9EJQcVCluUb+mx\nT0jZq+hCYb5XVYfnPzWcdmgwgTL8aFB7npwGhgDTRLqIJa7+XoJgjSJm+T0h\n0wr6oqr6+AOoh7UL7FAgm9gRSFYB6DlPFlUgp06A0KZfLJKa+qkiMKarG1Cf\nYO5aEcin1zhOwVkmzJHgp/C5V2sArJuThbM7bYuKbiNFQM2MiNBSXQUEKxqV\nYt4NLm4PUtwmAagKRq9M4DkbIXjPhPy4QjH2XpQncnPA/EikUnJ0wbbDBGMw\nvvThHGANce+6F6zE4NnCpVfr2RbRfKdCkqG3E6uUjOH4nDamlyh+hldWox2s\n9+QCwbcVirKIcPAP91s1+mag2liEyLzJ41B0OgBpUTU5ydXXyfrMqj79XNd3\npxrBfiTsdcrQ5KiHnc9mf3PuY6PlqBcf9nuJitugrv0tLbEyEbQwh32FkHNp\nVkrXaB2xZoxKFoXtXo4EiB9yJUuHIgV9I1MYghamSrVb5wGzAdRrRfxQS01H\n5VpEqTqr22p4z7ChhL6yYKAOVdNe3YRWZVZAqDvbCPQroxtVkHKyJmtfh2lQ\ndhiWOtjKGHLEaaI5Zaqh+tUqSdkF5AIVRMVZEeeyGenbnWFzMx6aWD1Vn75H\n4XKow0EtjToyATiEwpBCB7D8tppPR29NSUldnj3YnOMMcwxTg2vfQa7daPO7\nmGF4\r\n=xLdK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFJesANXfF81tC3mv8CNUDB1ubBlqhL0F57wgJgIlKQMAiAn1OOKGbdkMAcL6XwMN2HsGf6zPDG95NEwm4puQEyXUQ=="}]},"_npmUser":{"name":"rdardour","email":"romain@hull.io"},"directories":{},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"michaloo","email":"michal@raczka.me"},{"name":"timliuhull","email":"tim@hull.io"},{"name":"andyh565","email":"andy@hull.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.15_1614077757464_0.2045099067384908"},"_hasShrinkwrap":false},"0.14.0-beta.30":{"name":"hull","version":"0.14.0-beta.30","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=8.x"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:flow && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"flow check","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && babel src -d lib","dev":"babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.3.5","aws-sdk":"^2.849.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.7.2","body-parser":"^1.19.0","bull":"^3.20.1","cache-manager":"2.11.1","connect":"^3.7.0","connect-timeout":"^1.9.0","csv-stream":"^0.2.0","datadog-metrics":"^0.9.1","debug":"^3.2.7","del":"^2.2.2","dogapi":"^2.8.3","ejs":"^2.7.4","hull-client":"^2.0.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.6","kue":"^0.11.6","kue-ui":"^0.1.0","lodash":"^4.17.21","moment":"^2.29.1","newrelic":"^7.1.1","passport":"^0.4.1","progress-bar-webpack-plugin":"^1.12.1","promise-streams":"^1.0.1","promisepipe":"^2.1.3","raven":"^2.6.4","raw-body":"^2.4.1","react-hot-loader":"^4.13.0","request":"^2.72.0","sns-validator":"^0.3.4","sqs-consumer":"^3.8.0","supply":"0.0.4","urijs":"^1.19.6","uuid":"^3.4.0","webpack":"^3.12.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.25.0"},"peerDependencies":{"express":"^4.17.1","newrelic":"^4.13.1"},"devDependencies":{"babel":"^6.5.2","babel-cli":"^6.26.0","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.26.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.3.3","eslint":"^3.19.0","eslint-config-airbnb-base":"^11.3.2","eslint-plugin-flowtype":"^2.50.3","eslint-plugin-import":"^2.22.1","flow-bin":"^0.145.0","flow-typed":"^2.6.2","husky":"^0.14.3","isparta":"^4.1.1","minihull":"^3.0.2","mkdirp":"^0.5.5","mocha":"^3.5.3","nock":"^9.6.1","node-mocks-http":"^1.10.1","nyc":"^11.9.0","rimraf":"^2.7.1","sinon":"^2.4.1","sinon-chai":"^2.14.0","superagent":"^3.8.3","supertest":"^3.4.2","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"43cd3127774052acef9d8bb9568255870eec2314","_id":"hull@0.14.0-beta.30","_nodeVersion":"12.13.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-dFckqSJ+qjQ5NzeNOaV69tPwqXxXLqNSRa/4+ue/mPRuvY5UEHywjZyMD0aXHlnorHfAHoRqSZUknua7Y+sCOw==","shasum":"c135943f59c7acf2974cb29d9e824e867874793b","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0-beta.30.tgz","fileCount":112,"unpackedSize":589790,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNN9sCRA9TVsSAnZWagAA+78QAIb/ef19bKhtNo33w7zD\ncIZYFXaZo/ike4QxA8eJFXdt8Ys3e2Oba8q7S0SR2WgrZ69zeCEbXNwZUgLn\n8tKbRkNjew5LkYcZ7uObHchIsWheIwHljsddCuRe4irxru42olEr/S17b9SO\n7JQ5BUoIQg3FOQleCnjhJKKu7zFgX5+2feXSewyAWUH3JFXht3GlA14VtqqT\nct8O+EZPr4PaVD4h7wJI/52u1y6+p0LSUvb/freNLOHV9g00Un5ESi9IUovk\nSN93P31ysrcvVwHDaUSNK42KO2rMV2lNEBDbxcROqbAGLxbNvRicji6a4aqz\n4mrVsF5qjJ9qhxaKfJnbP/28SI7N+ZTgvkisb6iaP6HjEYhuPtgBFZ86tLWO\nYR59d7hIYDVg4gi2Oa9Ma5dUX+FibZnqUq/Vqu2l+Zmunw5e+Tnn+RqMXOJW\nZJ3ICuPSe/0qwtNGedCHtV4qrhCQ4vDoVtjeKnOE4Q01usfkhtYz92fZ1mM0\nwGd+BHD/mMUaVN0FNjOulUz6PiuMtJFoX7G8jMNJ0O0nEw2tGzwguZ4olnAz\n/X9UJII7ecYIWPD0mnPRtVxhQ7xg+WXAfjFmblT+TjlZ3Lny3EDKZu+o437h\nmU/5FvfoNmn4AKY3foWb+tAHu1Gj7hGY9+3EfV6TY/eDteGZa5D8kwNjGNVq\ng9N8\r\n=dBJm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2fFYpsfJFW3D44uTXI5oIu1OKBOUyKGceiDsbGM8rnQIgDTpCmCxLSjTlCjy4czFpvehGsr6zql/7c5aw1VXTeF0="}]},"_npmUser":{"name":"rdardour","email":"romain@hull.io"},"directories":{},"maintainers":[{"name":"rdardour","email":"romain@hull.io"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"michaloo","email":"michal@raczka.me"},{"name":"timliuhull","email":"tim@hull.io"},{"name":"andyh565","email":"andy@hull.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0-beta.30_1614077803845_0.7295191700222217"},"_hasShrinkwrap":false},"0.13.24":{"name":"hull","version":"0.13.24","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=14.x"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"echo flow test","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && npx babel src -d lib","dev":"npx babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^2.5.6","hull-client":"1.2.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","lodash":"^4.17.5","newrelic":"^7.1.1","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^7.1.1"},"devDependencies":{"babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"7c56f98a3f47babcd8a6a947424d774a375cf52a","_id":"hull@0.13.24","_nodeVersion":"12.22.7","_npmVersion":"6.14.15","dist":{"integrity":"sha512-c1zIHuTO21dmsNkugSepGm4D9/v0pKa20iDypjeiVMh2LSBAFuqFRiO37jm2nCVwdsA9uOF/zmr657qxiuma9w==","shasum":"e24326af0a4f21e2391345cc1131509331c13c4b","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.24.tgz","fileCount":97,"unpackedSize":403649,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDtmn/WdY/d7cw7NM8N0CA7X3y27dbF/FEV4Rjh+UudIwIgTMXQDeQRQpnT/mHMi1SdJfw00+1PE9SoRYAuTwc+yCo="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJifN/YACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp/6w/+JbtJsVJI9KLThazsl9Lf87B+ijmvQZwHP8LJzre5WquosM5L\r\nVx1R2gWEbOhpPz7Tp2hViCFTu9JtYNmPUK8w6HXUrIIkgIA++eG+Xy+NH1ze\r\nJIuCas4vyou7U0zuf/Fp+JbzSnELNm4PlZIjbiP4if0T0wv2d5VxzNP+McoR\r\nrVvtYrMA2K/RvBu2ftFdKoNuWzYa9x600lQc7kb+NIl6krQlK8RJofRclAT+\r\nYhUiNKAGLvryQRqma4NKqDVlCYy5yKJlO3vfPNtYV1mjyG3Ibt+7UAwVCoQG\r\nKThWy1XziOftDajQ+OlOgfF5E16T1MJeIQkd/Hipx0AAhPEk84iMFksNYhkW\r\nrPH0Vt1JPzJYO4a2utWWCp6p9n23qfMzwlGUjsQHEQDaefYReLqZHJaUBHoI\r\n7rDBZD9nwOhRibxqad5vul885euaNmoq/uiJTU1oiGGBnhHJxG8wVa8DGhyO\r\nzrx5I8ZqjVecXynn53cx3cQMPTm+xPOKq3WBia41QGYwufgdVN3B1BxiN0Vg\r\nXn8cekcWjlR3V/eqYrJY1lkpTHXoRRu8ukESEHspYqKxDOvhTu5wTHJA+lpc\r\nMh9WtG4GSt+YAyMfsghaHF7x1v6BOG7HmD3STCrVojckT4vIMjsu6G+Uvxre\r\nyg3i0z6xHxXZt46pRe9ZFxzihnE+dY0MsOc=\r\n=Djag\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"},"directories":{},"maintainers":[{"name":"rdardour","email":"romain@unity.fr"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"mathieuravaux","email":"mathieu.ravaux@gmail.com"},{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.24_1652350936656_0.30122955297478105"},"_hasShrinkwrap":false},"0.13.25":{"name":"hull","version":"0.13.25","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=14.x"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require babel-register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require babel-register -R spec ./test/integration/*.js","test:flow":"echo flow test","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && npx babel src -d lib","dev":"npx babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"JSONStream":"^1.1.2","aws-sdk":"^2.81.0","babel-polyfill":"^6.26.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"^0.4.0","del":"^2.2.1","dogapi":"^2.6.0","ejs":"^3.1.7","hull-client":"1.2.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","lodash":"^4.17.5","newrelic":"^7.1.1","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","request":"^2.72.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","supply":"0.0.4","urijs":"^1.18.7","webpack":"^3.12","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.22.2"},"peerDependencies":{"express":"^4.16.3","newrelic":"^7.1.1"},"devDependencies":{"babel-cli":"^6.24.1","babel-eslint":"^7.1.1","babel-plugin-transform-flow-comments":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.26.0","babel-register":"^6.9.0","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^6.1.0","eslint":"^3.2.2","eslint-config-airbnb-base":"^11.1.0","eslint-plugin-flowtype":"^2.39.1","eslint-plugin-import":"^2.2.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","isparta":"^4.0.0","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","superagent":"^3.8.2","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"gitHead":"3adf6fe74d0315b8ac0e644c86b461e1fde1a9c1","_id":"hull@0.13.25","_nodeVersion":"14.19.2","_npmVersion":"8.10.0","dist":{"integrity":"sha512-1tVDeOUYvzew2RbV2NomjerGnF2lsbCFtwhF7Of9QMJO3uiVvwyV+sHGMa8UnzMY5B7V9r/7zE/lUodPSS3Pfg==","shasum":"a4199a258e05f47dcaf853c4d98bf95e120d74b2","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.25.tgz","fileCount":97,"unpackedSize":403649,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIENY2bWIv9I/VTPcktxlvdFt5UVMwe84K8ndofkiMd3hAiEAqahEfMErmAhm4ta6K/V1Dq3DZcNVrf1BkscfhCnIY6w="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJihO8pACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr3cQ/+IiANW6DB/nA5zIMvjybGVYv3vHTE80HnKvG2e4jffy2UehH0\r\nYLFc0h5e7ftdvud3TlcrkMy+/6AT1qdi/z9Au/myJaDbxYgx6isDh2G2ouil\r\n8ReaCdZKWQQrmvcKmDIKCBWgQblOHSLVMeMtGNtCZkHM4MzZn/08OcKwLECU\r\nSU18SykWt54+k7bZXb5gInM48n5ldCTRUSsJgzvN+I8squh4ts2k27O3Kkw2\r\n4ue/qMWC9GugWMYf4DKg9uvTJhgNLRPBvYnl7fCtPg8rREBTQiEmcyeO8yZo\r\nQN/bZZXQLJRja95Occ68XuugJST1iWZ5TKm2pMDr8syjLEZZTu4HkY158rtM\r\n6lTyXdUr/sjok7P0CLGzp1JjdUSX3skB3Fdq8FeSfkwsEXodEMk0LWKQS2zr\r\nbTSSjb+HS63xkPgw5Sj0i1Q4ux87l5ow+bIbWTusalvIfNRFtE+MWo1umEE+\r\nQ2Njhnp5WM+D0DLUjiaGP9fSbcCB/5ZxHNQD+fAcgiXTUnRiVQ1HimgM6+sM\r\nsbK/WWG9Bztp5X9MtZ29Pis59lAx40xTRjnBn7WD5/XBOJWBQR7cRRrUjgnS\r\nl3bDOyLmQJLm4qQjo9WMJtvRs6nWKDVLn90iL6ENyxewekGIcJ8UCxfRUZzE\r\nSWU9iODhGfaFxceRA4n+ohq9moZXvC6g94A=\r\n=W2s+\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"},"directories":{},"maintainers":[{"name":"rdardour","email":"romain@unity.fr"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"mathieuravaux","email":"mathieu.ravaux@gmail.com"},{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.25_1652879145287_0.9791527906812414"},"_hasShrinkwrap":false},"0.13.26":{"name":"hull","version":"0.13.26","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=14.x"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require @babel/register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require @babel/register -R spec ./test/integration/*.js","test:flow":"echo flow test","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && npx babel src -d lib","dev":"npx babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"@babel/polyfill":"^7.12.1","JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"0.9.3","del":"^2.2.1","dogapi":"2.8.4","ejs":"^3.1.7","hull-client":"1.2.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","kue":"^0.11.5","lodash":"^4.17.5","newrelic":"^7.5.2","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","superagent":"^7.1.3","supply":"0.0.4","urijs":"^1.18.7","webpack":"^4.46.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.25.1"},"peerDependencies":{"express":"^4.16.3","newrelic":"^7.1.1"},"devDependencies":{"@babel/cli":"^7.17.10","@babel/core":"^7.18.2","@babel/eslint-parser":"^7.18.2","@babel/plugin-proposal-object-rest-spread":"^7.18.0","@babel/plugin-transform-flow-comments":"^7.17.12","@babel/plugin-transform-react-jsx":"^7.17.12","@babel/register":"^7.17.7","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^13.2.5","eslint":"^8.16.0","eslint-config-airbnb-base":"^15.0.0","eslint-plugin-flowtype":"^8.0.3","eslint-plugin-import":"^2.26.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"resolutions":{"ngrok":"^4.3.1"},"gitHead":"5577c9076c710dc66f2befb23aa7712bba3eafd5","_id":"hull@0.13.26","_nodeVersion":"14.19.2","_npmVersion":"8.10.0","dist":{"integrity":"sha512-oHwn0k2kCAv70yMjLRsPfOtyT89STKoNISjqHBqJmaDypbYh7VeWyIAGTlz4yB+mGPKckFkKVH9PF3Cs9tVdpw==","shasum":"f71f675d29955e0898cd2559e88aa9367ef4e58c","tarball":"https://registry.npmjs.org/hull/-/hull-0.13.26.tgz","fileCount":98,"unpackedSize":810371,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFofzpvCyyZlMoaABvHfR21IlBnIJ8XF8tj3ZyBtT580AiEA6YFvECqionKNXzaZhkN7H0xyLSVhONPWXK7HsEp41Xk="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJimf6YACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpKqw//S+EBNE55E9FRogbs5utcVA1NM0R/skPWe3NRC1PtN6JoKaju\r\nsTNVAMior9N1l5HaK2zhZOmimLQQGhDpPnm9a+AKU8CV6E/jMbR0swi7P6LF\r\nZd1yVJEWbGp1hxWUmUKs1wyEpjcd4GuDxjdpb3RBLHFqQJRpPMtb4qQ4OyUq\r\no14Jx1Xl/8b2PHYw9hnduQGdMc7fYHIOHWiOohJJdj+GkgGuwgrGKMCv2r4z\r\n/isphb2FfGnYiw5tpHLIZJGmcz66xakbwv7RHfaUo45CbtsP6r6B+qTNQmX/\r\n2n9aCEkcKruyH6vtbYPLWvGgc2XZ9Z0kSaEaUV7ta49szrY64QMIbPepcJTH\r\nXP4sI4F1uLEXBDuqZfZHVYabEmamhWghHFZjAjAsL4PLgrK6kO4X0/LBKNfo\r\nUUMg4EBX01UZ3qg7X2IDfvt60jNRyD3BHJGeWqadea7GAXHyUSZPZ2MDhwOY\r\n/5DcHut1VxiNK0lTIRQdm6OiMrDsnpwb0JdqkzMGXuzVlq8axilpgjG5lQfn\r\nIo+suKAtZkkcMADASQDtkjtZfY26PBi2ZkpwKPd4hKh56EtxJOBf47QuJ1ME\r\n89PtRQmGlVMyV574Chaa1SFia7KOuVIMlXja9cKTg0L14mWIaesxss6M06ws\r\nGd6+Zn2g0b0tRyYFqnCIjkGCSQeGJ18waIE=\r\n=dZ7W\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"},"directories":{},"maintainers":[{"name":"rdardour","email":"romain@unity.fr"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"mathieuravaux","email":"mathieu.ravaux@gmail.com"},{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.13.26_1654259352040_0.4266382238026827"},"_hasShrinkwrap":false},"0.14.0":{"name":"hull","version":"0.14.0","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=14.x"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require @babel/register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require @babel/register -R spec ./test/integration/*.js","test:flow":"echo flow test","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && npx babel src -d lib","dev":"npx babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"@babel/polyfill":"^7.12.1","JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"0.9.3","del":"^2.2.1","dogapi":"2.8.4","ejs":"^3.1.7","hull-client":"^2.0.4","jsonwebtoken":"^7.4.3","jwt-simple":"^0.5.0","lodash":"^4.17.5","newrelic":"^7.5.2","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","superagent":"^7.1.3","supply":"0.0.4","urijs":"^1.18.7","webpack":"^4.46.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.25.1"},"peerDependencies":{"express":"^4.16.3","newrelic":"^7.1.1"},"devDependencies":{"@babel/cli":"^7.17.10","@babel/core":"^7.18.2","@babel/eslint-parser":"^7.18.2","@babel/plugin-proposal-object-rest-spread":"^7.18.0","@babel/plugin-transform-flow-comments":"^7.17.12","@babel/plugin-transform-react-jsx":"^7.17.12","@babel/register":"^7.17.7","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^13.2.5","eslint":"^8.16.0","eslint-config-airbnb-base":"^15.0.0","eslint-plugin-flowtype":"^8.0.3","eslint-plugin-import":"^2.26.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"resolutions":{"ngrok":"^4.3.1"},"gitHead":"a10c8e4cb622adfc76aaffc331cd7ea79a942b1c","_id":"hull@0.14.0","_nodeVersion":"14.19.2","_npmVersion":"8.10.0","dist":{"integrity":"sha512-YKA1R3zXmELIzPSB7VLAqbAZVeAmxemvcbboJ6uW6RKxVOYBhyHCiGzyQXh6g1U4i04rcB69xL5xjpCLe3t2+Q==","shasum":"c6d21428520790de79488469a3b9d825b076194d","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.0.tgz","fileCount":17,"unpackedSize":684027,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHJI8zSriiPz4Zdb/S5FBlIC0FLCqma5ln2BlwmgSVLZAiAjUqC7mtdBnoTJM+cLzr9RT9c8Xr8804DGCo/wKTBbEg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJimh9CACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoH0A//RknDSp7LdX1ck3U4o4O/AhGIcPmz0Dbz//4cR3IcvQKfpliz\r\nuTodGtHh2LLc4sF6jhEjegQeQUHCBzEeg/ZIEqURMGxYkxZrlTdXLP14dro3\r\nkEw1GZ5DCMPJNdntfAsOQZXjv8WynFLH55b/aoETvB61Hj1uXFNFExG1XBTu\r\nW7WsYtSDob9+3uMSkagusw5nUZtFBn9d6+jsfjjZW7W4NVc70upXFB0tK5Ax\r\nPtD7V4LRtXUmpzSiEdnzSCdWrtdGCJGbFhjnuy+993zVjQTxfWYcvm/2AjvL\r\n5i6vkH1evtUsATBcmZ4J6YWBLz57l7tWPOX2wpSlZDmd/eWhyChja9eOfhm9\r\n50qk/H7fZxRkWjxk7zAX8Zz6+uXgWX+eNzUc3qnanvIuOVO6yXOxhVU3pK22\r\nBJvZBvBhGGka1jFGaMG+WEgefvixeEvLK9zvpdX8EUELnQlb7MdrjY5sWo1u\r\nRirECvmrmC4vSjaBys+sPfxgHY67N78fql/Hm1/TOi2H9U1rBfrwXRirmdkq\r\nFdS9oU4XgTvcYsxjB0kAUYXg80NLL9nnlMJl47C1V4WKQauky4YBr2xI4oHv\r\nS7futEP5NWDsqe+CmNaCTQYlYOTvtXumdl0ZP+VzZPvMEZVOrYl+AU8pEB2f\r\nj8v07KI0ZsktEnNtpatWhSQNoUbIwABnaUo=\r\n=YVpz\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"},"directories":{},"maintainers":[{"name":"rdardour","email":"romain@unity.fr"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"mathieuravaux","email":"mathieu.ravaux@gmail.com"},{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.0_1654267714330_0.03626847821707857"},"_hasShrinkwrap":false},"0.14.1":{"name":"hull","version":"0.14.1","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=14.x"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require @babel/register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require @babel/register -R spec ./test/integration/*.js","test:flow":"echo flow test","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && npx babel src -d lib","dev":"npx babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"@babel/polyfill":"^7.12.1","JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"0.9.3","del":"^2.2.1","dogapi":"2.8.4","ejs":"^3.1.7","hull-client":"^2.0.4","jsonwebtoken":"^8.5.1","jwt-simple":"^0.5.0","lodash":"^4.17.5","newrelic":"^7.5.2","passport":"^0.3.2","progress-bar-webpack-plugin":"^1.11.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","superagent":"^7.1.3","supply":"0.0.4","urijs":"^1.18.7","webpack":"^5.73.0","webpack-dev-middleware":"^2.0.6","webpack-hot-middleware":"^2.25.1"},"peerDependencies":{"express":"^4.16.3","newrelic":"^7.1.1"},"devDependencies":{"@babel/cli":"^7.17.10","@babel/core":"^7.18.2","@babel/eslint-parser":"^7.18.2","@babel/plugin-proposal-object-rest-spread":"^7.18.0","@babel/plugin-transform-flow-comments":"^7.17.12","@babel/plugin-transform-react-jsx":"^7.17.12","@babel/register":"^7.17.7","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^13.2.5","eslint":"^8.16.0","eslint-config-airbnb-base":"^15.0.0","eslint-plugin-flowtype":"^8.0.3","eslint-plugin-import":"^2.26.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"resolutions":{"ngrok":"^4.3.1"},"gitHead":"e2f666812175255b913904765de2ada1ddd301c7","_id":"hull@0.14.1","_nodeVersion":"14.19.2","_npmVersion":"8.10.0","dist":{"integrity":"sha512-suxSerlBGwMurgxAr50qwYUMlSEvu8F0J3b9O8+64iaId9aL96P9b8xTSsM4M3RNuIQa+C109WXvPOAVyV9e9Q==","shasum":"7f76ad7a658ae109c5d89934bd7932db6f870437","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.1.tgz","fileCount":97,"unpackedSize":808340,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCXnDtNm1FgRDZxc8bJognfql6AEHiZcC7nXiixaKv6vwIhALrj3YyWFU2QET2RkQ2bD6sbavrragijLpcL0Dxd4aaf"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJimjn7ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqaKg//WzmeDXN7Wbve0CM4XOxsRx+0Tpic+k5zNPGmRHfYI9dAkm0J\r\nGrvLd9KEcAS+8HqqyUF/mjvIHqaNdKyRmRq0QOow8YxOajabXCqijhhrr2gP\r\nA28U/80Q1pbv+ocUfdZY7fTc2N5WSWpkjA49siKc1JVctgFOvPfUb+6YLVJp\r\nc0VpZcNi5mqBkYenvUgKXJ0UDrgndSwpYAuT4DE5Bsw8z0scxNbxaGZ0LIrh\r\nUMlqBxr5r3HJFxGDLDZb41jj9lVqWfrU3rd9YtPuYnWHMjm2RkAaxOPMAI5V\r\nuLlJDRjke17x4n6sVT7kmXoO3jU5nUdETwHbpcQXshjgMC2sMNdK/bc3F7s5\r\nafg2ZOUxK+dmfJpi/IHpMS0r3wwQxcfRCFOL7x8OUQXqD20vpmeOSR19EmGv\r\nd/OZil6Ao1BnTbX9uQiOsBtBVso9KN9qevHmdP7D83wcEURxQXG6JcgQ5f9j\r\nIrY3s78EoeJRz50EXZ08YltJ61jk2MaccrnGPhWOMFGr6kYf3EZy10JZfCnY\r\nKWJpjJj79nKSCBcqgw99le8Ch88MssIexE0p6QK6/a4lzSq2AaoMn9aQ+38c\r\nQ31vrwtEG/yWWoWLT4xp0gt1nvEFiM8m+fonmdhplXZAlm44X8c1DQRby6t1\r\nua9+Ml+yjjNMyyXbRMY0eZdJCAjCcZdhy5E=\r\n=C/5G\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"},"directories":{},"maintainers":[{"name":"rdardour","email":"romain@unity.fr"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"mathieuravaux","email":"mathieu.ravaux@gmail.com"},{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.1_1654274554886_0.19063449794988285"},"_hasShrinkwrap":false},"0.14.2":{"name":"hull","version":"0.14.2","description":"A Node.js client for hull.io","main":"lib","repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"homepage":"https://github.com/hull/hull-node","bugs":{"url":"https://github.com/hull/hull-node/issues"},"engines":{"node":">=14.x"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"license":"MIT","scripts":{"test":"npm run test:lint && npm run test:unit && npm run test:integration","test:lint":"eslint src && documentation lint src","test:modules":"npm outdated --depth=0","test:unit":"NODE_ENV=test mocha --require @babel/register -R spec ./test/unit/*.js ./test/unit/**/*.js","test:integration":"NODE_ENV=test mocha --require @babel/register -R spec ./test/integration/*.js","test:flow":"echo flow test","test:coverage":"nyc --babel-cache --hook-run-in-context false npm run test","update":"updtr","clean":"rimraf lib","build":"npm run clean && npx babel src -d lib","dev":"npx babel src -d lib -w","prepublish":"npm run build","documentation":"documentation build src -f md -o API.md --access public --markdownToc=false","precommit":"npm run documentation && git add API.md"},"dependencies":{"@babel/polyfill":"^7.12.1","JSONStream":"^1.1.2","aws-sdk":"^2.81.0","basic-auth":"^1.1.0","batch-stream":"^0.1.3","bluebird":"^3.4.7","body-parser":"^1.15.2","bull":"^3.0.0-rc.3","cache-manager":"2.6.0","connect":"^3.4.1","connect-timeout":"^1.8.0","csv-stream":"^0.1.3","datadog-metrics":"0.9.3","del":"^2.2.1","dogapi":"2.8.4","ejs":"^3.1.7","hull-client":"^2.0.4","jsonwebtoken":"^8.5.1","jwt-simple":"^0.5.0","lodash":"^4.17.5","newrelic":"^7.5.2","passport":"^0.3.2","progress-bar-webpack-plugin":"^2.1.0","promise-streams":"^1.0.1","raven":"^2.4.2","raw-body":"^2.1.7","react-hot-loader":"^4.2.0","sns-validator":"^0.3.0","sqs-consumer":"^3.6.1","superagent":"^7.1.3","supply":"0.0.4","urijs":"^1.18.7","webpack":"^5.73.0","webpack-dev-middleware":"^5.3.3","webpack-hot-middleware":"^2.25.1"},"peerDependencies":{"express":"^4.16.3","newrelic":"^7.1.1"},"devDependencies":{"@babel/cli":"^7.17.10","@babel/core":"^7.18.2","@babel/eslint-parser":"^7.18.2","@babel/plugin-proposal-object-rest-spread":"^7.18.0","@babel/plugin-transform-flow-comments":"^7.17.12","@babel/plugin-transform-react-jsx":"^7.17.12","@babel/register":"^7.17.7","chai":"^3.5.0","chai-http":"^3.0.0","documentation":"^13.2.5","eslint":"^8.16.0","eslint-config-airbnb-base":"^15.0.0","eslint-plugin-flowtype":"^8.0.3","eslint-plugin-import":"^2.26.0","flow-bin":"^0.68.0","flow-typed":"^2.4.0","husky":"^0.14.3","minihull":"^2.1.1","mkdirp":"^0.5.1","mocha":"^3.0.0","nock":"^9.2.3","node-mocks-http":"^1.7.0","nyc":"^11.0.3","rimraf":"^2.6.0","sinon":"^2.2.0","sinon-chai":"^2.10.0","supertest":"^3.1.0","updtr":"^1.0.0"},"nodeBoilerplateOptions":{"mochaGlobals":["stub","spy","expect"]},"nyc":{"include":["src"]},"resolutions":{"ngrok":"^4.3.1"},"gitHead":"97bb0364b922d4c7ef493f579bad031c9471292e","_id":"hull@0.14.2","_nodeVersion":"14.19.2","_npmVersion":"8.10.0","dist":{"integrity":"sha512-qzlGYJ5mrH4/+78ExlTNpszK68+Xb+gUhYQOmEYUg3NfiShblfGC54Pl9ONmAoanxG1lmoC5zLrznmMAnf9dJg==","shasum":"adf79611ca439bc9737eb9a54d5232a67138a25f","tarball":"https://registry.npmjs.org/hull/-/hull-0.14.2.tgz","fileCount":97,"unpackedSize":808315,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFpA+jV2Br22rGkw0ZMrYRiqefGkZp2PPgPqOXyHXGTCAiBGELY/Ny28XhbGVkX9Txd/6wC/EuS19/YoeRxRqOr07w=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiqGzYACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr/iQ/+IpRQOWh2X9IjrMW6z9bpsffAkWf20aJPn2Xeiv9DIxO51VH6\r\nc03StxcvwdTd4uNXVBlUkRyL4hI8ok5tfgl2YthhN52s7ICoxd5ZoYEevOYO\r\nji5/SCqs11l/4M6V3yjTnU7PofUn8TfvK2JvNEauZMkOaDRL9DZwoskmBVBa\r\nm5ZVzCBEQNztoUQH7NhB3NUDuXh8N+vmmz66zDXbhfaj/z8yvq1aP8JMtUsj\r\nT05il+mTDnjONVzXsAKVAkvA/BVFvFfD3gFfD6C8ZPd8GJX16epws8I3VelK\r\n6VYU4x9qU0akDNAJtYe9TFzKNYQKarR+2DBfrgeP7zD9TNh9cmjX18diphWB\r\niChwtqAnldGCASHFllASDVyzTsRYkz51OGYLZrokrOTqivMhW4rRS9NHTozk\r\nBWuOya86ReH+VBtay6DSrSVh6XRRzjVk7E6YV0y+A7fSV8jJSywz7zT5a5aH\r\nzZ+FVuE/Ivr74VvheTZ/sJqwN/rSoUv6eGwnrSKJbyb8t8QL1SuQHpG/p+uj\r\n/yc4aPrIVXLHFgpMkywIquuhV6OWeFtfD2KcliA46n1fa3DhIB07w4smSRl1\r\nhEOJpqyFIY6a0byogmpxv1GJZ2DMkBXS8Eqzrm4d6xg4c8PT+kRntgaRkUAV\r\ndrLeQhivIS4kXad3E9W4mvcjWeL2AI4gyXA=\r\n=3948\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"},"directories":{},"maintainers":[{"name":"rdardour","email":"romain@unity.fr"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"mathieuravaux","email":"mathieu.ravaux@gmail.com"},{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/hull_0.14.2_1655205080584_0.309901247051938"},"_hasShrinkwrap":false}},"readme":"# Overview\n\n## [Hull Client](https://github.com/hull/hull-client-node)\nA low level Hull Platform API client. Refer to it's documentation for more details\n\n```javascript\nconst hullClient = new Hull.Client({ configuration });\n```\n\nThis is an example of the bare bones API client. Please refer to [it's own Github repository](https://github.com/hull/hull-client-node) for documentation.\n\n## [Hull Middleware](#hullmiddleware)\n\n```javascript\napp.use(Hull.Middleware({ configuration }));\n```\n\nA bridge between Hull Client and a NodeJS HTTP application (e.g. express) which initializes HullClient a context for every HTTP request. See example usage below.\nA standalone usage is possible (it's a strandard ExpressJS middleware), but if there is no specific reason to do so, the recommended way of building connectors is [Hull Connector](#hullconnector).\n\n## [Hull Connector](#hullconnector)\n\n```javascript\nconst connector = new Hull.Connector({ configuration });\n```\n\nA complete toolkit which is created next to ExpressJS server instance. Includes Hull Middleware and a set of official patterns to build highly scalable and efficient Connectors.\n\nTo get started see few chapters of this README first:\n\n1. start with [Initialization](#initialization) and [Setup Helpers](#setup-helpers)\n2. then have a quick look what you hava available in [Context Object](#context-object)\n3. proceed to [Incoming data flow](#incoming-data-flow) or [Outgoing data flow](#outgoing-data-flow) depending on your use case\n\n![hull node core components](/docs/assets/hull-node-components.png)\n\n---\n\n# Hull.Middleware\n\n> Example usage\n\n```javascript\nconst Hull = require(\"hull\");\nconst express = require(\"express\");\n\nconst app = express();\napp.use(Hull.Middleware({ hostSecret: \"secret\" }));\napp.post(\"/show-segments\", (req, res) => {\n  req.hull.client.get(\"/segments\").then(segments => {\n    res.json(segments);\n  });\n});\n```\n\nThis middleware standardizes the instantiation of a [Hull Client](https://github.com/hull/hull-client-node) in the context of authorized HTTP request. It also fetches the entire Connector's configuration. As a result it's responsible for creating and exposing a [Context Object](#base-context), another important part is how this middleware decide where to look for configuration settings (connector ID, SECRET and ORGANIZATION) which then are applied to HullClient, for details please refer to [configuration resolve strategy](#configuration-resolve-strategy).\n\nFor configuration options refer to [API REFERENCE](./API.md#hullmiddleware).\n\n---\n\n# Hull.Connector\n\nThis is the smallest possible Nodejs connector implementation:\n\n```javascript\nconst app = express();\napp.get(\"/manifest.json\", serveTheManifestJson);\napp.listen(port);\n```\n\nAs you can see connector is a simple HTTP application served from public address. It can be implemented in any way and in any technological stack as long as it implements the same API. You can find more details on connector's structure [here](https://www.hull.io/docs/apps/ships/).\n\n## Initialization\n\n```javascript\nconst Hull = require(\"hull\");\n\nconst connector = new Hull.Connector({\n  port: 1234, // port to start express app on\n  hostSecret: \"secret\", // a secret generated random string used as a private key\n});\n```\n\nThis is the instance of the `Connector` module which exposes a set of utilities which can be applied to the main [express](http://expressjs.com/) app. All configuration options are listen in [API REFERENCE](./API.md#hullconnector)\n\nThe utilities and special middlewares can be taken one-by-one from the library and applied to the application manually, but to make the whole process easier, there are two helper methods that set everything up for you:\n\n### Setup Helpers\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\nconst connector = new Hull.Connector({ hostSecret, port });\n\nconnector.setupApp(app); // apply connector related features to the application\napp.post(\"/fetch-all\", (req, res) => {\n  // req.hull is the full Context Object!\n  req.hull.client.get(\"/segments\")\n    .then((segments) => {\n      res.json(segments);\n    });\n});\nconnector.startApp(app); // apply termination middlewares and internally calls `app.listen`\n```\n\nSetup Helpers are two high-level methods exposed by initialized Connector instances to apply custom middlewares to the Express application. Those middlewares enrich the request object with full [Context Object](#context-object).\n\nTo get more details on how those helpers methods work please see [API REFERENCE](./API.md#setupapp)\n\n---\n\n# Context Object\n\n[Hull.Connector](#hullconnector) apply multiple middlewares to the request handler, including [Hull.Middleware](#hullmiddleware). The result is a **Context Object** that's available in all action handlers and routers as `req.hull`. It's a set of parameters and modules to work in the context of current organization and connector instance. This Context is divided into a base set by `Hull.Middleware` (if you use it standalone) and an extended set applied when using `Hull.Connector` and helpers method described above.\n\nHere is the base structure of the Context Object (we also provide Flow type for this object [here](./src/types/hull-req-context.js)).\n\n```javascript\n{\n  // set by Hull.Middleware\n  requestId: \"\",\n  config: {},\n  token: \"\",\n  client: { // Instance of \"new Hull.Client()\"\n    logger: {},\n  },\n  ship: {\n    //The values for the settings defined in the Connector's settings tab\n    private_settings: {},\n    settings: {}\n  },\n  hostname: req.hostname,\n  options: req.query + req.body,\n\n  // set by Hull.Connector\n  connectorConfig: {},\n  segments: [],\n  users_segments: [],\n  accounts_segments: [],\n\n  cache: {},\n  enqueue: () => {},\n  metric: {},\n  helpers: {},\n  service: {},\n  message: {},\n  notification: {}\n  smartNotifierResponse: {}\n}\n```\n\n## Base Context - set by Hull.Middleware\n\n### **requestId**\n\nunique identifier for a specific request. Will be used to enrich the context of all the logs emitted by the the [Hull.Client logger](https://github.com/hull/hull-client-node/#setting-a-requestid-in-the-logs-context). This value is automatically added by the `notifHandler` and `smartNotifierHandler` with the SNS `MessageId` or SmartNotifier `notification_id`.\n\n### **config**\n\nan object carrying `id`, `secret` and `organization`. You can setup it prior to the Hull Middleware execution (via custom middleware) to override default [configuration strategy](#configuration-resolve-strategy).\n\n### **token**\n\nan encrypted version of configuration. If it's already set in the request, Hull Middleware will try to decrypt it and get the configuration from it. If it's not available at the beginning and middleware resolved the configuration from other sources it will encrypt it and set `req.hull.token` value.\n\nWhen the connector needs to send the information outside the Hull ecosystem it has to use the token, not to expose the raw credentials. The usual places where this happens are:\n\n- dashboard links\n- oAuth flow (callback url)\n- external incoming webhooks\n\n### **client**\n\n[Hull API client](https://github.com/hull/hull-client-node) initialized to work with current organization and connector.\n\n### **ship**\n\nship object with manifest information and `private_settings` fetched from the Hull Platform.\n`ship` is the legacy name for Connectors.\n\n### **hostname**\n\nHostname of the current request. Since the connector are stateless services this information allows the connector to know it's public address.\n\n### **options**\n\nIs the object including data from `query` and `body` of the request\n\n## Extended Context - set by `Hull.Connector`\n\n### **connectorConfig**\n\nHash with connector settings, details in Hull.Connector [constructor reference](./API.md#hullconnector).\n\n### **segments**\n\n```json\n[\n  {\n    \"name\": \"Segment name\",\n    \"id\": \"123abc\"\n  }\n]\n```\n\nAn array of segments defined at the organization, it's being automatically exposed to the context object.\nThe segment flow type is specified [here](./src/types/hull-segment.js).\n\n`users_segments` param is alias to `segments` and `accounts_segments` exposes list of segments for accounts.\n\n### **cache**\n\nSince every connector can possibly work on high volumes of data performing and handling big number of requests. Internally the cache is picked by the `Hull Middleware` to store the `ship object` and by `segmentsMiddleware` to store `segments list`. The cache can be also used for other purposes, e.g. for minimizing the External API calls. `Caching Module` is exposing three public methods:\n\n```javascript\nctx.cache.get(\"object_name\");\nctx.cache.set(\"object_name\", objectValue);\nctx.cache.wrap(\"object_name\", (objectValue) => {\n  return Promise.resolve(objectValue);\n});\n```\n\n[Full API reference](./API.md#cache)\n\n### **enqueue**\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nreq.hull.enqueue(\"jobName\", { user: [] }, options = {});\n```\n\nA function added to context by `Queue Module`. It allows to perform tasks in an async manner. The queue is processed in background in a sequential way, it allows to:\n\n- respond quickly in the express application actions (they just queue the work)\n- split the workload into smaller chunks (e.g. for extract parsing)\n- control the concurrency - most of the SERVICE APIs have rate limits\n\n[Full API reference](./API.md#enqueue)\n\n### **metric**\n\nAn object added to context by `Instrumentation Module`. It allows to send data to metrics service. It's being initiated in the right context, and expose following methods:\n\n```javascript\nreq.hull.metric.value(\"metricName\", metricValue = 1);\nreq.hull.metric.increment(\"metricName\", incrementValue = 1); // increments the metric value\nreq.hull.metric.event(\"eventName\", { text = \"\", properties = {} });\n```\n\nAn object added to context by the `Instrumentation Module`. It allows to send data to the metrics service. [Full API reference](./API.md#metric)\n\n### **helpers**\n\nHelpers are just a set of simple functions added to the Context Object which make common operation easier to perform. They all follow [context management convention](#context-management-convention) but the functions can be also used in a standalone manner:\n\n```javascript\nconst { updateSettings } = require(\"hull/lib/helpers\");\n\napp.post(\"/request\", (req, res) => {\n  updateSettings(req.hull, { called: true });\n  // or:\n  req.hull.helpers.updateSettings({ called: true });\n});\n```\n\n\nBeside of connector setting updating, they also simplify working with [outgoing extracts](#batch-extracts).\n\nAll helpers are listed in [API REFERENCE](./API.md#helpers)\n\n### **service**\n\nA namespace reserved for connector developer to inject a custom modules. When the connector base code evolves, the best technique to keep it maintainable is to split it into a set of functions or classes. The `service` namespace is reserved for the purpose and should be used together with `use` method on connector instance to apply custom middleware. That should be an object with custom structure adjusted to specific connector needs and scale:\n\n```javascript\n// custom middleware creating the `service` param\nconnector.use((req, res, next) => {\n  req.hull.service = {\n    customFunction: customFunction.bind(req.hull),\n    customModule: new CustomClass(req.hull)\n  };\n  next();\n});\n\nconnector.setupApp(app);\n\napp.get(\"/action\", (req, res) => {\n  const { service } = req.hull;\n  service.customFunction(req.query.user_id);\n  // or\n  service.customModule.customMethod(req.query.user_id);\n});\n```\n\nWe strongly advice to follow our [context management convention](#context-management-convention) which make it easy to keep functions and classes signatures clean and standard.\n\n### **message**\n\nOptional - set if there is a sns message incoming.\n\nIt contains the raw, message object - should not be used directly by the connector, `req.hull.notification` is added for that purpose.\n\n```javascript\nType: \"Notification\",\nSubject: \"user_report:update\",\nMessage: \"{\\\"user\\\":{}}\"\n```\n\n### **notification**\n\nOptional - if the incoming message type if `Notification`, then the messaged is parsed and set to notification.\n\n```javascript\nsubject: \"user_report:update\",\ntimestamp: new Date(message.Timestamp),\npaload: { user: {} }\n```\n\n### **smartNotifierResponse**\n\nUse setFlowControl to instruct the Smart notifier how to handle backpressure.\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: 'next',\n  size: 100,\n  in: 5000\n});\n```\n\n## Configuration resolve strategy\n\nDuring `Context Object` building important step is how Hull Client configuration is read. The whole strategy is descibed below step-by-step.\n\nHere is what happens when your Express app receives a query:\n\n1. If a config object is found in `req.hull.config` steps **2** and **3** are skipped.\n2. If a token is present in `req.hull.token`, the middleware will try to use the `hostSecret` to decrypt it and set `req.hull.config`.\n3. If the query string (`req.query`) contains `id`, `secret`, `organization`, they will be stored in `req.hull.config`.\n4. After this, if a valid configuration is available in `req.hull.config`, a Hull Client instance will be created and stored in `req.hull.client`.\n5. When this is done, then the Ship will be fetched and stored in `req.hull.ship`\n\n  If there is a `req.hull.cache` registered in the Request Context Object, it will be used to cache the ship object. For more details see [Context Object Documentation](#context)\n\n6. If the configuration or the secret is invalid, an error will be thrown that you can catch using express error handlers.\n\n## Custom middleware\n\nThe `Hull.Connector` architecture gives a developer 3 places to inject custom middleware:\n\n1. At the very beginning of the middleware stack - just after `const app = express();` - this is a good place to initialy modify the incoming request, e.g. set the `req.hull.token` from custom property\n2. After the [Context Object](#context) is built - after calling `setupApp(app)` - all context object would be initiated, but `req.hull.client`, `req.hull.segments` and `req.hull.ship` will be present **only if** credentials are passed.\n3. Before the closing `startApp(app)` call which internally calls `app.listen()`\n\n## Context management convention\n\nThe [context object](#context-object) is treated by the `Hull.Connector` as a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container which carries on all required dependencies to be used in actions, jobs or custom methods.\n\nThis library sticks to a the following convention of managing the context object:\n\n### Functions\n\nAll functions take context as a first argument:\n\n```javascript\nfunction getProperties(context, prop) {\n  cons { client } = context;\n  return client.get(\"/properties\", { prop });\n}\n```\n\nThis allow binding such functions to the context and using bound version\n\n```javascript\nconst getProp = getProperties.bind(null, context);\ngetProp(\"test\") === getProperties(context, \"test\");\n```\n\n### Classes\n\nIn case of a class the context is the one and only argument:\n\n```javascript\nclass ServiceAgent {\n  constructor(context) {\n    this.client = context.client;\n  }\n}\n```\n\nAll functions and classes listed in [API reference](./API.md) and available in the [context object](#context-object) follow this convention when used from contex object they will be already bound, so you don't need to provide the first argument when using them.\n\n---\n\n# Incoming data flow\n\nTo get data into platform we need to use `traits` or `track` methods from `HullClient` (see details [here](https://github.com/hull/hull-client-node/#methods-for-user-or-account-scoped-instance)). When using `Hull.Connector` we have the client initialized in the correct context so we can use it right away in side any HTTP request handler.\n\nLet's write the simplest possible HTTP endpoint on the connector to fetch some users:\n\n```javascript\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.get(\"/fetch-users\", (req, res) => {\n  const ctx = req.hull;\n  const { api_key } = ctx.ship.private_settings;\n\n  // let's try to get some data from 3rd party API\n  const customApiClient = new CustomApiClient(api_key);\n\n  customApiClient.fetchUsers()\n    .then(users => {\n      return users.map((user) => {\n        return ctx.client.asUser(user.ident).traits(user.attributes);\n      });\n    })\n    .then(() => {\n      res.end(\"ok\");\n    });\n});\n\nconnector.startApp(app);\n```\n\nThen we can create a button on the connector dashboard to run it or call it from any other place. The only requirement is that the enpoint is called with credentials according to the [configuration resolve strategy](#configuration-resolve-strategy).\n\n## Schedules\n\nIf you want to run specific endpoint with a selected interval you can use `schedules` param of the manifest.json:\n\n```json\n{\n \"schedules\": [\n    {\n      \"url\": \"/fetch-users\",\n      \"type\": \"cron\",\n      \"value\": \"*/5 * * * *\"\n    }\n  ]\n}\n```\n\nThis way selected connector endpoint would be run at every 5th minute.\n\n---\n\n# Outgoing data flow\n\nTo peform operations on in response to new data coming in or being updated on Hull Platform we use two means of communications - [notifications](#notifications) which are triggered on per user/event/change basis or [batch extracts](#batch-extracts) which can be sent manually from the dashboard UI or requested by the connector.\n\n## Notifications\n\nAll events triggered on user base result in a notification hitting specified connector endpoint. Current Hull Connector version supports two generations of those notifications - legacy and new \"smart-notifier\". Following guide assume you are using the new generation.\n\nTo subscribe to platform notifications, define the endpoint in connector manifest.json:\n\n```json\n{\n  \"tags\": [\"smart-notifier\"],\n  \"subscriptions\": [{\n    \"url\": \"/smart-notifier\"\n  }]\n}\n```\n\nThen in ExpressJS server definition we need to pick `smartNotifierHandler` from `utils` directory:\n\n```javascript\nconst { smartNotifierHandler } = require(\"hull/lib/utils\");\n\nconst app = express();\nconst connector = new Hull.Connector();\n\nconnector.setupApp(app);\n\napp.use(\"/smart-notifier\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages = []) => {\n      // more about `smartNotifierResponse` below\n      ctx.smartNotifierResponse.setFlowControl({\n        type: \"next\",\n        size: 100,\n        in: 5000\n      });\n      return Promise.resolve();\n    }\n  },\n  options: {\n    groupTraits: false\n  }\n}));\n\nconnector.startApp(app);\n```\n\nThe `user:update` handler will be run with batches of notification messages coming from platform. User update message is a json object which is grouping together all events and changes which happened on the specic user since the previous notification. The structure of the single message is defined in [this Flow Type](./src/types/hull-user-update-message.js).\n\nInside the handler you can use any object from the [Context Object](#context-object). Remember that the handler needs to return a valid promise at the end of it's operations.\n\nFull information on `smartNotifierHandler` is available in [API REFERENCE](./API.md#smartnotifierhandler).\n\n### FlowControl\n\n`Smart-notifier` generation of notifications delivery allows us to setup `flow control ` which define pace at which connector will be called with new messages:\n\n```javascript\nctx.smartNotifierResponse.setFlowControl({\n  type: \"next\", // `next` or `retry`, defines next flow action\n  size: 1000, // only for `next` - number of messages for next notification\n  in: 1000, // delay for next flow step in ms\n  at: 1501062782 // time to trigger next flow step\n});\n```\n\nFlowControl is an element of the `SmartNotifierResponse`. When the HTTP response is built it has the following structure\n\n```javascript\n// response body:\n{\n  flow_control: {\n    type: \"next\",\n    in: 1000\n  },\n  metrics: []\n}\n```\n\nThe Defaults are the following:\n\n```javascript\n// for a resolved, successful promise:\n{\n  type: \"next\",\n  size: 1,\n  in: 1000\n}\n\n// for a rejected, erroneous promise:\n{\n  type: \"retry\",\n  in: 1000\n}\n```\n\n## Batch extracts\n\nSecond way of operating on Hull user base it to process batch extracts.\n\nIn addition to event notifications Hull supports sending extracts of users and accounts. These extracts can be triggered via manual user action on the dashboard or can be programmatically requested from the Connector logic (see [requestExtract helper](./API.md#requestextract)). The Connector will expose the manual batches action if your `manifest.json` includes a `batch` or `batch-accounts` tag :\n\n```json\n{\n  \"tags\" : [ \"batch\", \"batch-accounts\" ]\n}\n```\n\nIn both cases the batch extracts are processed by the `user:update` and `account:update` handlers. The extract is split into smaller chunks using `options.maxSize`. Only traits and segments are exposed in the extracted lines, `events` and `changes` are never sent.\n\nIn addition, to let the handler function detect whether it is processing a batch extract or notifications, a third argument is passed- in case of notifications it is `undefined`, otherwise it includes `query` and `body` parameters from `req` object.\n\nNotification of batches, when the extracts are ready are sent as a `POST` request on the `/batch` and `/batch-accounts` endpoints respectively.\n\n\n```javascript\napp.post(\"/batch\", notifHandler({\n  options: {\n    maxSize: 100,\n    groupTraits: false\n  },\n  handers: {\n    \"user:update\": ({ hull }, users) => {\n      hull.logger.info(\"Get users\", users);\n    }\n  }\n}));\n```\n\n---\n\n# Connector status\n\nPlatform API comes with an endpoint where connector can post it's custom checks performed on settings and/or 3rd party api.\nThe resulted should be posted to an endpoint but for testing and debugging purposes we also respond with the results.\n\nHere comes example status implementation:\n\n```javascript\napp.all(\"/status\", (req, res) => {\n  const { ship, client } = req.hull;\n  const messages = [];\n  let status = \"ok\";\n\n  const requiredSetting = _.get(ship.private_settings, \"required_setting\");\n  if (code === undefined) {\n    status = \"warning\";\n    messages.push(\"Required setting is not set.\");\n  }\n\n  res.json({ messages, status });\n  return client.put(`${req.hull.ship.id}/status`, { status, messages });\n});\n```\n\nThen to make it being run in background we can use a schedule entry in `manifest.json`:\n\n```json\n{\n  \"schedules\": [\n    {\n      \"url\": \"/status\",\n      \"type\": \"cron\",\n      \"value\": \"*/30 * * * *\"\n    }\n  ]\n}\n```\n\n---\n\n# Installation & Authorization\n\nFirst step of connector installation is done automatically by the platform and the only needed part from connector end is manifest.json file.\n\nHowever typically after the installation we want that the connector is authorized with the 3rd party API.\n\nHull Node comes with packaged authentication handler using Passport - the utility is called oAuthHandler and you can find documentation [here](./API.md#oauthhandler).\n\n---\n\n# Utilities\n\nBeside of `Hull.Connector` class and `Context Object` all other public API elements of this library is exposed as `Utils` which are standalone functions to be picked one-by-one and used in custom connector code.\n\nList of all utilities are available [here](./API.md#utils)\n\n## Superagent plugins\n\nHull Node promotes using [SuperAgent](http://visionmedia.github.io/superagent/) as a core HTTP client. We provide two plugins to add more instrumentation over the requests.\n\n- [superagentErrorPlugin](./API.md#superagenterrorplugin)\n- [superagentInstrumentationPlugin](./API.md#superagentinstrumentationplugin)\n- [superagentUrlTemplatePlugin](./API.md#superagenturltemplateplugin)\n\n---\n\n# Infrastructure\n\nThe connector internally uses infrastructure modules to support its operation on application process level and provide some of the [Context Object](#context-object) elements like `cache`, `metric` and `enqueue`. See following API REFERENCE docs to see what is the default behavior and how to change it:\n\n- [Instrumentation](./API.md#instrumentationagent) (for gathering metrics)\n- [Cache](./API.md#cacheagent) (for caching ship object, segment lists and custom elements)\n- [Queue](./API.md#queueagent) (for internal queueing purposes)\n- Batcher (for internal incoming traffing grouping)\n\n---\n\n# Worker\n\nMore complex connectors usually need a background worker to split its operation into smaller tasks to spread the workload.\n\n**This is generally a deprecated idea and should not be implemented in new connectors. Fluent flow control and smartNotifierHandler should be used instead.**\n\n```javascript\nconst express = require(\"express\");\nconst Hull = require(\"hull\");\n\nconst app = express();\n\nconst connector = new Hull.Connector({ hostSecret });\n// apply connector related features to the application\nconnector.setupApp(app);\n\nconnector.worker({\n  customJob: (ctx, payload) => {\n    // process payload.users\n  }\n});\napp.post(\"/fetch-all\", (req, res) => {\n  req.hull.enqueue('customJob', { users: [] });\n});\nconnector.startApp(app, port);\nconnector.startWorker((queueName = 'queueApp'));\n```\n\n---\n\n# Flow annotations\n\nWhen using a [flow](https://flow.org) enabled project, we recommend using flow types provided by hull-node. You can import them in your source files directly from `hull` module and use `import type` flow structure:\n\n```javascript\n// @flow\nimport type { THullObject } from \"hull\";\n\nparseHullObject(user: THullObject) {\n  // ...\n}\n```\n\nSee [`src/lib/types`](./src/lib/types) directory for a full list of available types.\n\n---\n\n# Error handling\n\nAll handlers for outgoing traffic are expecting to return a promise. Resolution or rejection of the promise triggers different behavior of error handling.\nDefault JS errors are treated as [unhandled errors](#unhandled-error), the same applies for any unhandled exceptions thrown from the handler code.\n\nHull Connector provides two other error classes [TransientError](#transient-error) and [LogicError](#logic-error) which are handled\nby internals of the SDK in a different way.\n\nThe convention is to filter known issues and categorize them into transient or logic errors categories. All unknown errors will default to unhandled errors.\n\n## Unhandled error\n\nDefault, native Javascript error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 500\nsentry exception report | yes\ndatadog metrics | no\n\n```javascript\napp.use(\"/smart-notifier-handler\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return Promise.reject(new Error(\"Error message\"));\n    }\n  }\n}));\n```\n\n\n## Transient error\n\nThis is an error which is known to connector developer. It's an error which is transient and request retry should be able to overcome the issue.\nIt comes with 3 subclasses to mark specifc scenarios which are related to time when the error should be resolved.\n\n- RateLimitError\n- ConfigurationError\n- RecoverableError\n\nThe retry strategy is currently the same as for unhandled error, but it's handled better in terms of monitoring.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | retry\nother endpoints response | error\nstatus code | 400\nsentry exception report | no\ndatadog metrics | yes\n\n```javascript\nconst { TransientError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier-handler\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      ctx.smartNotifierResponse.setFlowControl({\n\n      });\n\n      return Promise.reject(new TransientError(\"Error message\"));\n    }\n  }\n}));\n```\n\n## Logic error\n\nThis is an error which needs to be handled by connector implementation and as a result the returned promised **must not be rejected**.\n\n**IMPORTANT:** Rejecting or throwing this error without try/catch block will be treated as unhandled error.\n\ncontext | behavior\n--- | ---\nsmart-notifier response | next\nother endpoints response | success\nstatus code | 200\nsentry exception report | no\ndatadog metrics | no\n\n```javascript\nconst { LogicError } = require(\"hull/lib/errors\");\n\napp.use(\"/smart-notifier-handler\", smartNotifierHandler({\n  handlers: {\n    \"user:update\": (ctx, messages) => {\n      return (() => {\n        return Promise.reject(new LogicError(\"Validation error\"));\n      })\n      .catch((err) => {\n        if (err.name === \"LogicError\") {\n          // log outgoing.user.error\n          return Promise.resolve();\n        }\n        return Promise.reject(err);\n      });\n    }\n  }\n}));\n```\n\n\n","maintainers":[{"name":"rdardour","email":"romain@unity.fr"},{"name":"sbellity","email":"sbellity@gmail.com"},{"name":"mathieuravaux","email":"mathieu.ravaux@gmail.com"},{"name":"louis.jahn.hull","email":"louis.jahn@messagebird.com"}],"time":{"modified":"2022-06-18T23:08:55.260Z","created":"2013-06-04T01:14:36.312Z","0.1.0":"2013-06-04T01:14:36.786Z","0.3.0":"2013-06-19T16:36:22.836Z","0.3.1":"2014-02-05T13:02:11.961Z","0.3.2":"2014-02-27T17:10:02.287Z","0.4.0":"2014-06-11T10:31:35.780Z","0.4.1":"2014-08-30T04:34:24.555Z","0.5.0":"2015-11-18T13:08:49.872Z","0.5.1":"2015-12-28T11:58:41.460Z","0.5.2":"2016-02-03T20:22:25.919Z","0.5.3":"2016-02-04T17:37:05.287Z","0.5.4":"2016-02-05T11:06:02.952Z","0.5.5":"2016-02-23T12:05:17.108Z","0.6.0":"2016-02-27T23:37:45.291Z","0.6.1":"2016-03-01T11:45:54.729Z","0.6.2":"2016-03-22T16:28:45.630Z","0.6.3":"2016-04-14T10:33:23.122Z","0.6.4":"2016-04-14T12:04:55.637Z","0.6.5":"2016-04-18T14:44:52.237Z","0.6.6":"2016-04-18T15:12:11.485Z","0.6.7":"2016-04-21T15:52:06.914Z","0.6.8":"2016-04-28T15:28:08.080Z","0.6.9":"2016-05-02T11:31:55.789Z","0.6.10":"2016-05-08T09:59:50.904Z","0.7.0":"2016-06-21T11:50:26.604Z","0.7.1":"2016-06-21T13:33:46.973Z","0.7.2":"2016-06-21T16:34:38.416Z","0.7.3":"2016-06-24T10:17:34.069Z","0.7.4":"2016-06-26T07:27:45.530Z","0.8.0":"2016-07-08T17:17:35.661Z","0.9.0":"2016-07-18T11:26:37.662Z","0.9.1":"2016-07-18T12:05:58.620Z","0.9.2":"2016-07-18T14:43:03.835Z","0.9.3":"2016-07-18T17:23:23.745Z","0.9.4":"2016-07-29T10:22:57.276Z","0.9.5":"2016-08-02T13:25:09.020Z","0.9.6":"2016-08-04T12:39:58.681Z","0.9.7":"2016-09-22T18:13:50.917Z","0.10.0":"2016-09-28T12:12:03.464Z","0.10.1":"2016-09-30T09:48:04.873Z","0.10.2":"2016-10-11T12:42:45.318Z","0.10.3":"2016-11-02T21:04:07.271Z","0.10.4":"2016-11-10T16:56:37.690Z","0.10.5":"2016-12-15T09:14:22.187Z","0.10.6":"2017-01-20T13:26:27.896Z","0.10.7":"2017-02-17T12:48:35.127Z","0.10.8":"2017-02-25T13:10:37.010Z","0.11.0-beta.1":"2017-02-28T14:22:01.342Z","0.11.0-beta.2":"2017-03-20T12:55:50.833Z","0.11.0-beta.3":"2017-04-18T11:03:28.087Z","0.11.0-beta.4":"2017-04-27T15:36:05.734Z","0.11.0":"2017-05-04T12:39:29.777Z","0.11.1":"2017-05-25T09:52:31.001Z","0.11.2":"2017-05-29T15:01:17.088Z","0.11.3":"2017-06-08T15:40:19.712Z","0.11.4":"2017-06-19T14:19:51.191Z","0.11.5":"2017-06-22T15:35:18.518Z","0.11.6":"2017-06-27T12:57:52.281Z","0.11.7":"2017-06-30T10:50:40.000Z","0.11.8":"2017-07-05T10:33:40.663Z","0.11.9-beta.1":"2017-07-06T17:49:19.163Z","0.11.9":"2017-07-11T20:53:36.945Z","0.11.10":"2017-07-11T21:12:28.812Z","0.11.11":"2017-07-11T21:38:05.689Z","0.12.0-beta.1":"2017-07-18T10:33:16.243Z","0.12.0-beta.2":"2017-07-18T11:14:35.473Z","0.12.0-beta.3":"2017-07-18T14:29:38.046Z","0.11.12":"2017-07-21T08:57:46.922Z","0.12.0":"2017-07-21T11:42:08.374Z","0.13.0-beta.1":"2017-07-26T11:11:35.966Z","0.13.0-beta.2":"2017-07-27T09:59:49.911Z","0.13.0-beta.3":"2017-07-27T14:10:10.646Z","0.12.1":"2017-07-28T09:19:13.280Z","0.12.2":"2017-07-31T07:51:39.909Z","0.13.0-beta.4":"2017-07-31T10:21:39.684Z","0.13.0-beta.5":"2017-08-08T08:52:08.402Z","0.13.0-beta.6":"2017-08-08T13:08:04.818Z","0.13.0-beta.7":"2017-08-09T10:19:20.738Z","0.13.0-beta.8":"2017-08-09T10:31:38.873Z","0.13.0-beta.9":"2017-08-10T07:35:13.838Z","0.12.3":"2017-08-17T12:32:47.067Z","0.13.0-beta.10":"2017-08-17T19:02:51.443Z","0.12.4":"2017-08-19T17:26:35.104Z","0.11.13":"2017-08-19T17:35:06.262Z","0.13.0-beta.12":"2017-08-25T10:15:49.374Z","0.12.5":"2017-08-28T08:54:11.700Z","0.12.6":"2017-09-19T12:30:26.915Z","0.12.7":"2017-09-26T13:34:21.827Z","0.13.0":"2017-10-11T10:22:40.997Z","0.13.1":"2017-10-16T15:36:50.136Z","0.12.8":"2017-10-16T15:37:33.816Z","0.13.2":"2017-10-17T13:42:05.681Z","0.13.3":"2017-11-03T14:57:56.963Z","0.13.4":"2017-11-03T15:53:36.230Z","0.13.5":"2017-11-08T11:49:49.091Z","0.13.6":"2017-11-27T16:32:24.469Z","0.13.7":"2017-12-07T15:02:56.127Z","0.13.8":"2017-12-08T11:48:28.168Z","0.13.9":"2017-12-12T09:48:00.073Z","0.13.10":"2017-12-27T08:46:21.250Z","0.13.11":"2018-03-22T16:50:25.224Z","0.13.13":"2018-04-13T16:18:12.430Z","0.13.14":"2018-04-13T16:34:10.183Z","0.13.15":"2018-05-23T17:05:55.323Z","0.14.0-beta.1":"2018-05-25T09:46:35.707Z","0.13.16":"2018-06-07T08:34:02.121Z","0.14.0-beta.2":"2018-06-20T14:37:00.943Z","0.14.0-beta.3":"2018-07-03T10:38:46.174Z","0.14.0-beta.4":"2018-07-04T09:37:13.997Z","0.14.0-beta.5":"2018-07-04T14:14:49.563Z","0.14.0-beta.6":"2018-07-13T10:15:01.958Z","0.14.0-beta.7":"2018-07-18T12:45:32.530Z","0.14.0-beta.8":"2018-07-25T11:42:26.445Z","0.14.0-beta.9":"2018-07-25T13:48:48.178Z","0.14.0-beta.10":"2018-07-25T13:59:42.120Z","0.14.0-beta.11":"2018-07-25T14:04:11.015Z","0.14.0-beta.12":"2018-07-26T14:25:28.947Z","0.14.0-beta.13":"2018-07-27T13:27:07.678Z","0.14.0-beta.14":"2018-09-03T07:37:09.739Z","0.13.17":"2018-11-30T14:49:56.709Z","0.14.0-beta.1.1":"2018-12-19T21:57:05.936Z","0.13.18":"2019-03-15T08:50:15.765Z","0.13.19-beta":"2019-06-04T14:48:31.039Z","0.13.19":"2019-08-26T13:55:02.337Z","0.13.20":"2019-09-18T08:30:13.088Z","0.13.21":"2020-05-21T15:59:28.513Z","0.13.22":"2020-09-03T22:22:13.981Z","0.13.23":"2021-02-22T20:58:17.370Z","0.14.0-beta.15":"2021-02-23T10:55:57.671Z","0.14.0-beta.30":"2021-02-23T10:56:44.071Z","0.13.24":"2022-05-12T10:22:16.868Z","0.13.25":"2022-05-18T13:05:45.417Z","0.13.26":"2022-06-03T12:29:12.336Z","0.14.0":"2022-06-03T14:48:34.554Z","0.14.1":"2022-06-03T16:42:35.097Z","0.14.2":"2022-06-14T11:11:20.773Z"},"author":{"name":"Romain Dardour","email":"romain@hull.io"},"repository":{"type":"git","url":"git+https://github.com/hull/hull-node.git"},"readmeFilename":"README.md","bugs":{"url":"https://github.com/hull/hull-node/issues"},"license":"MIT","homepage":"https://github.com/hull/hull-node","users":{"rafszul":true}}