├── .eslintrc.json ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── opslevel.yml ├── package-lock.json ├── package.json └── test └── index.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "rules": { 8 | "indent": [ 9 | "error", 10 | 2 11 | ], 12 | "linebreak-style": [ 13 | "error", 14 | "unix" 15 | ], 16 | "quotes": [ 17 | "error", 18 | "single" 19 | ], 20 | "semi": [ 21 | "error", 22 | "always" 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Node ### 4 | # Logs 5 | logs 6 | *.log 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # node-waf configuration 23 | .lock-wscript 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 30 | node_modules 31 | 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # v4.1.4 3 | - Update the AWS SDK to at least version 2.178.0 which has an updated version of crypto-browserify which as vulnerable to Insecure Randomness due to using the cryptographically insecure Math.random(). See https://snyk.io/test/npm/aws-sdk/2.94.0. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Auth0, Inc. (http://auth0.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kinesis writable stream for [bunyan](http://npmjs.com/package/bunyan). 2 | 3 | ## Installation 4 | 5 | ```sh 6 | npm install aws-kinesis-writable --save 7 | ``` 8 | 9 | ## Usage 10 | 11 | ```javascript 12 | var KinesisWritable = require('aws-kinesis-writable'); 13 | 14 | var kinesis = new KinesisWritable({ 15 | accessKeyId: 'KEY_ID', 16 | secretAccessKey: 'SECRET_KEY', 17 | region: 'AWS_REGION', 18 | streamName: 'MyKinesisStream', 19 | partitionKey: 'MyApp' 20 | }); 21 | 22 | process.stdin.resume(); 23 | process.stdin.pipe(kinesis); 24 | ``` 25 | 26 | ### Configuration Parameters 27 | 28 | `buffer` (defaults to true): It can be a boolean or an object describing its conditions. 29 | 30 | This library uses by default an smart buffering approach. Messages are sent when one of the following conditions are meet: 31 | 32 | - X seconds after the last batch of messages sent. Default: 5 seconds. 33 | - X messages are queued waiting to be sent. Default: 10 messages. 34 | - a message has priority. Default: all messages do no have priority 35 | 36 | Example: 37 | ```javascript 38 | new KinesisWritable({ 39 | region: 'AWS_REGION', 40 | streamName: 'MyKinesisStream', 41 | partitionKey: 'foo', 42 | buffer: { 43 | timeout: 1, // Messages will be sent every second 44 | length: 100, // or when 100 messages are in the queue 45 | hasPriority: function (msg) { // or the message has a type > 40 46 | var entry = JSON.parse(msg); 47 | return entry.type > 40; 48 | } 49 | } 50 | }); 51 | ``` 52 | 53 | `partitionKey` can be either an string or a function that accepts a message and returns a string. By default it is a function that returns the current EPOCH (Date.now()). Example: 54 | 55 | ```javascript 56 | new KinesisWritable({ 57 | region: 'AWS_REGION', 58 | streamName: 'MyKinesisStream', 59 | partitionKey: function (msg) { 60 | var entry = JSON.parse(msg); 61 | return entry.level + '|' + entry.name; 62 | } 63 | }); 64 | ``` 65 | 66 | `streamName` is the name of the Kinesis Stream. 67 | 68 | ### Events 69 | 70 | * `error`: Emitted every time records are failed to be written. 71 | 72 | **Note**: Amazon Credentials are not required. It will either use the environment variables, `~/.aws/credentials` or roles as every other aws sdk. 73 | 74 | ## Issue Reporting 75 | 76 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 77 | 78 | ## Author 79 | 80 | [Auth0](auth0.com) 81 | 82 | ## License 83 | 84 | This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. 85 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | const assert = require('assert'); 3 | const Writable = require('stream').Writable; 4 | 5 | const retry = require('retry'); 6 | const AWS = require('aws-sdk'); 7 | const merge = require('lodash.merge'); 8 | const safeStringify = require('fast-safe-stringify'); 9 | 10 | /** 11 | * [KinesisStream description] 12 | * @param {Object} params 13 | * @param {string} [params.accessKeyId] AWS access key 14 | * @param {string} [params.secretAccessKey] AWS secret 15 | * @param {string} [params.sessionToken] AWS session token 16 | * @param {string} [params.credentials] AWS credentials (in lieu of separate credentials) 17 | * @param {string} [params.region] AWS region 18 | * @param {string} [params.endpoint] AWS HTTP endpoint 19 | * @param {string} [params.objectMode] True if Javascript objects can be directly written to Kinesis 20 | * (instead of strings) 21 | * @param {string} params.streamName AWS Knesis stream name 22 | * @param {function} params.partitionKey function that return the partitionKey based on a msg passed by argument 23 | * @param {object} [params.httpOptions={}] HTTP options that will be used on `aws-sdk` (e.g. timeout values) 24 | * @param {number} [params.buffer.timeout] Max. number of seconds 25 | * to wait before send msgs to stream 26 | * @param {number} [params.buffer.length] Max. number of msgs to queue 27 | * before send them to stream. 28 | * @param {@function} [params.buffer.isPrioritaryMsg] Evaluates a message and returns true if msg has priority (to be deprecated) 29 | * @param {@function} [params.buffer.hasPriority] Evaluates a message and returns true if msg has priority 30 | * @param {@function} [params.buffer.retry.retries] Attempts to be made to flush a batch 31 | * @param {@function} [params.buffer.retry.minTimeout] Min time to wait between attempts 32 | * @param {@function} [params.buffer.retry.maxTimeout] Max time to wait between attempts 33 | */ 34 | 35 | const defaultBuffer = { 36 | timeout: 5, 37 | length: 10, 38 | hasPriority: function() { 39 | return false; 40 | }, 41 | retry: { 42 | retries: 2, 43 | minTimeout: 300, 44 | maxTimeout: 500 45 | } 46 | }; 47 | 48 | function isLambda() { 49 | return !!( 50 | (process.env.LAMBDA_TASK_ROOT && process.env.AWS_EXECUTION_ENV) || 51 | false 52 | ); 53 | } 54 | 55 | function KinesisStream (params) { 56 | assert(params.streamName, 'streamName required'); 57 | 58 | this.streamName = params.streamName; 59 | this.buffer = merge(defaultBuffer, params.buffer); 60 | this.partitionKey = params.partitionKey || function getPartitionKey() { 61 | return Date.now().toString(); 62 | }; 63 | 64 | this.hasPriority = this.buffer.isPrioritaryMsg || this.buffer.hasPriority; 65 | 66 | // increase the timeout to get credentials from the EC2 Metadata Service 67 | if (!isLambda()) { 68 | AWS.config.credentials = new AWS.EC2MetadataCredentials({ 69 | httpOptions: { timeout: 5000 } 70 | }); 71 | } 72 | 73 | this.recordsQueue = []; 74 | 75 | this.kinesis = params.kinesis || new AWS.Kinesis({ 76 | accessKeyId: params.accessKeyId, 77 | secretAccessKey: params.secretAccessKey, 78 | sessionToken: params.sessionToken, 79 | credentials: params.credentials, 80 | region: params.region, 81 | endpoint: params.endpoint, 82 | objectMode: params.objectMode, 83 | httpOptions: params.httpOptions 84 | }); 85 | 86 | Writable.call(this, { objectMode: params.objectMode }); 87 | } 88 | 89 | util.inherits(KinesisStream, Writable); 90 | 91 | function parseChunk(chunk) { 92 | if (Buffer.isBuffer(chunk) ) { 93 | chunk = chunk.toString(); 94 | } 95 | if (typeof chunk === 'string') { 96 | chunk = JSON.parse(chunk); 97 | } 98 | return chunk; 99 | } 100 | 101 | KinesisStream.prototype._write = function(chunk, enc, next) { 102 | chunk = parseChunk(chunk); 103 | 104 | const hasPriority = this.hasPriority(chunk); 105 | if (hasPriority) { 106 | this.recordsQueue.unshift(chunk); 107 | } else { 108 | this.recordsQueue.push(chunk); 109 | } 110 | 111 | if (this.recordsQueue.length >= this.buffer.length || hasPriority) { 112 | if (this.timer) { 113 | clearTimeout(this.timer); 114 | this.timer = null; 115 | } 116 | this.flush(); 117 | } else if (!this.timer) { 118 | this.timer = setTimeout(this.flush.bind(this), this.buffer.timeout * 1000); 119 | } 120 | 121 | return next(); 122 | }; 123 | 124 | KinesisStream.prototype.dispatch = function(records, cb) { 125 | if (records.length === 0) { 126 | return cb ? cb() : null; 127 | } 128 | 129 | const operation = retry.operation(this.buffer.retry); 130 | 131 | const formattedRecords = records.map((record) => { 132 | const partitionKey = typeof this.partitionKey === 'function' 133 | ? this.partitionKey(record) 134 | : this.partitionKey; 135 | return { Data: safeStringify(record), PartitionKey: partitionKey }; 136 | }); 137 | 138 | operation.attempt(() => { 139 | this.putRecords(formattedRecords, (err) => { 140 | if (operation.retry(err)) { 141 | return; 142 | } 143 | 144 | if (err) { 145 | this.emitRecordError(err, records); 146 | } 147 | 148 | if (cb) { 149 | return cb(err ? operation.mainError() : null); 150 | } 151 | }); 152 | }); 153 | }; 154 | 155 | KinesisStream.prototype.putRecords = function(records, cb) { 156 | this.kinesis.putRecords({ 157 | StreamName: this.streamName, 158 | Records: records 159 | }, cb); 160 | }; 161 | 162 | KinesisStream.prototype.flush = function() { 163 | // reset timer so that next enqueue will start it again. 164 | this.timer = null; 165 | this.dispatch(this.recordsQueue.splice(0, this.buffer.length)); 166 | }; 167 | 168 | KinesisStream.prototype.emitRecordError = function (err, records) { 169 | err.records = records; 170 | this.emit('error', err); 171 | }; 172 | 173 | module.exports = KinesisStream; 174 | -------------------------------------------------------------------------------- /opslevel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1 3 | repository: 4 | owner: platform_core_services 5 | tier: 6 | tags: 7 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-kinesis-writable", 3 | "version": "4.2.4", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@sinonjs/formatio": { 8 | "version": "2.0.0", 9 | "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", 10 | "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", 11 | "dev": true, 12 | "requires": { 13 | "samsam": "1.3.0" 14 | } 15 | }, 16 | "@sinonjs/samsam": { 17 | "version": "2.0.0", 18 | "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-2.0.0.tgz", 19 | "integrity": "sha512-D7VxhADdZbDJ0HjUTMnSQ5xIGb4H2yWpg8k9Sf1T08zfFiQYlaxM8LZydpR4FQ2E6LZJX8IlabNZ5io4vdChwg==", 20 | "dev": true 21 | }, 22 | "abbrev": { 23 | "version": "1.0.9", 24 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", 25 | "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", 26 | "dev": true 27 | }, 28 | "align-text": { 29 | "version": "0.1.4", 30 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", 31 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", 32 | "dev": true, 33 | "optional": true, 34 | "requires": { 35 | "kind-of": "^3.0.2", 36 | "longest": "^1.0.1", 37 | "repeat-string": "^1.5.2" 38 | } 39 | }, 40 | "amdefine": { 41 | "version": "1.0.1", 42 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", 43 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", 44 | "dev": true 45 | }, 46 | "argparse": { 47 | "version": "1.0.10", 48 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 49 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 50 | "dev": true, 51 | "requires": { 52 | "sprintf-js": "~1.0.2" 53 | } 54 | }, 55 | "assertion-error": { 56 | "version": "1.1.0", 57 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 58 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 59 | "dev": true 60 | }, 61 | "async": { 62 | "version": "1.5.2", 63 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 64 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 65 | "dev": true 66 | }, 67 | "aws-sdk": { 68 | "version": "2.610.0", 69 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.610.0.tgz", 70 | "integrity": "sha512-kqcoCTKjbxrUo2KeLQR2Jw6l4PvkbHXSDk8KqF2hXcpHibiOcMXZZPVe9X+s90RC/B2+qU95M7FImp9ByMcw7A==", 71 | "requires": { 72 | "buffer": "4.9.1", 73 | "events": "1.1.1", 74 | "ieee754": "1.1.13", 75 | "jmespath": "0.15.0", 76 | "querystring": "0.2.0", 77 | "sax": "1.2.1", 78 | "url": "0.10.3", 79 | "uuid": "3.3.2", 80 | "xml2js": "0.4.19" 81 | } 82 | }, 83 | "balanced-match": { 84 | "version": "1.0.0", 85 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 86 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 87 | "dev": true 88 | }, 89 | "base64-js": { 90 | "version": "1.3.1", 91 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 92 | "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" 93 | }, 94 | "brace-expansion": { 95 | "version": "1.1.11", 96 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 97 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 98 | "dev": true, 99 | "requires": { 100 | "balanced-match": "^1.0.0", 101 | "concat-map": "0.0.1" 102 | } 103 | }, 104 | "browser-stdout": { 105 | "version": "1.3.1", 106 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 107 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 108 | "dev": true 109 | }, 110 | "buffer": { 111 | "version": "4.9.1", 112 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", 113 | "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", 114 | "requires": { 115 | "base64-js": "^1.0.2", 116 | "ieee754": "^1.1.4", 117 | "isarray": "^1.0.0" 118 | } 119 | }, 120 | "camelcase": { 121 | "version": "1.2.1", 122 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", 123 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", 124 | "dev": true, 125 | "optional": true 126 | }, 127 | "center-align": { 128 | "version": "0.1.3", 129 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", 130 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", 131 | "dev": true, 132 | "optional": true, 133 | "requires": { 134 | "align-text": "^0.1.3", 135 | "lazy-cache": "^1.0.3" 136 | } 137 | }, 138 | "chai": { 139 | "version": "4.1.2", 140 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", 141 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", 142 | "dev": true, 143 | "requires": { 144 | "assertion-error": "^1.0.1", 145 | "check-error": "^1.0.1", 146 | "deep-eql": "^3.0.0", 147 | "get-func-name": "^2.0.0", 148 | "pathval": "^1.0.0", 149 | "type-detect": "^4.0.0" 150 | } 151 | }, 152 | "check-error": { 153 | "version": "1.0.2", 154 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 155 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 156 | "dev": true 157 | }, 158 | "cliui": { 159 | "version": "2.1.0", 160 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", 161 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", 162 | "dev": true, 163 | "optional": true, 164 | "requires": { 165 | "center-align": "^0.1.1", 166 | "right-align": "^0.1.1", 167 | "wordwrap": "0.0.2" 168 | }, 169 | "dependencies": { 170 | "wordwrap": { 171 | "version": "0.0.2", 172 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", 173 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", 174 | "dev": true, 175 | "optional": true 176 | } 177 | } 178 | }, 179 | "commander": { 180 | "version": "2.15.1", 181 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 182 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 183 | "dev": true 184 | }, 185 | "concat-map": { 186 | "version": "0.0.1", 187 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 188 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 189 | "dev": true 190 | }, 191 | "debug": { 192 | "version": "3.1.0", 193 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 194 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 195 | "dev": true, 196 | "requires": { 197 | "ms": "2.0.0" 198 | } 199 | }, 200 | "decamelize": { 201 | "version": "1.2.0", 202 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 203 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 204 | "dev": true, 205 | "optional": true 206 | }, 207 | "deep-eql": { 208 | "version": "3.0.1", 209 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 210 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 211 | "dev": true, 212 | "requires": { 213 | "type-detect": "^4.0.0" 214 | } 215 | }, 216 | "deep-is": { 217 | "version": "0.1.3", 218 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 219 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 220 | "dev": true 221 | }, 222 | "diff": { 223 | "version": "3.5.0", 224 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 225 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 226 | "dev": true 227 | }, 228 | "escape-string-regexp": { 229 | "version": "1.0.5", 230 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 231 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 232 | "dev": true 233 | }, 234 | "escodegen": { 235 | "version": "1.8.1", 236 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", 237 | "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", 238 | "dev": true, 239 | "requires": { 240 | "esprima": "^2.7.1", 241 | "estraverse": "^1.9.1", 242 | "esutils": "^2.0.2", 243 | "optionator": "^0.8.1", 244 | "source-map": "~0.2.0" 245 | } 246 | }, 247 | "esprima": { 248 | "version": "2.7.3", 249 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", 250 | "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", 251 | "dev": true 252 | }, 253 | "estraverse": { 254 | "version": "1.9.3", 255 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", 256 | "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", 257 | "dev": true 258 | }, 259 | "esutils": { 260 | "version": "2.0.2", 261 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 262 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 263 | "dev": true 264 | }, 265 | "events": { 266 | "version": "1.1.1", 267 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 268 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 269 | }, 270 | "fast-levenshtein": { 271 | "version": "2.0.6", 272 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 273 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 274 | "dev": true 275 | }, 276 | "fast-safe-stringify": { 277 | "version": "2.0.6", 278 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", 279 | "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==" 280 | }, 281 | "fs.realpath": { 282 | "version": "1.0.0", 283 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 284 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 285 | "dev": true 286 | }, 287 | "get-func-name": { 288 | "version": "2.0.0", 289 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 290 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 291 | "dev": true 292 | }, 293 | "glob": { 294 | "version": "5.0.15", 295 | "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", 296 | "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", 297 | "dev": true, 298 | "requires": { 299 | "inflight": "^1.0.4", 300 | "inherits": "2", 301 | "minimatch": "2 || 3", 302 | "once": "^1.3.0", 303 | "path-is-absolute": "^1.0.0" 304 | } 305 | }, 306 | "growl": { 307 | "version": "1.10.5", 308 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 309 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 310 | "dev": true 311 | }, 312 | "handlebars": { 313 | "version": "4.0.11", 314 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", 315 | "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", 316 | "dev": true, 317 | "requires": { 318 | "async": "^1.4.0", 319 | "optimist": "^0.6.1", 320 | "source-map": "^0.4.4", 321 | "uglify-js": "^2.6" 322 | }, 323 | "dependencies": { 324 | "source-map": { 325 | "version": "0.4.4", 326 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", 327 | "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", 328 | "dev": true, 329 | "requires": { 330 | "amdefine": ">=0.0.4" 331 | } 332 | } 333 | } 334 | }, 335 | "has-flag": { 336 | "version": "1.0.0", 337 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", 338 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", 339 | "dev": true 340 | }, 341 | "he": { 342 | "version": "1.1.1", 343 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 344 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 345 | "dev": true 346 | }, 347 | "ieee754": { 348 | "version": "1.1.13", 349 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 350 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 351 | }, 352 | "inflight": { 353 | "version": "1.0.6", 354 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 355 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 356 | "dev": true, 357 | "requires": { 358 | "once": "^1.3.0", 359 | "wrappy": "1" 360 | } 361 | }, 362 | "inherits": { 363 | "version": "2.0.3", 364 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 365 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 366 | "dev": true 367 | }, 368 | "is-buffer": { 369 | "version": "1.1.6", 370 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 371 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", 372 | "dev": true, 373 | "optional": true 374 | }, 375 | "isarray": { 376 | "version": "1.0.0", 377 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 378 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 379 | }, 380 | "isexe": { 381 | "version": "2.0.0", 382 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 383 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 384 | "dev": true 385 | }, 386 | "istanbul": { 387 | "version": "0.4.5", 388 | "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", 389 | "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", 390 | "dev": true, 391 | "requires": { 392 | "abbrev": "1.0.x", 393 | "async": "1.x", 394 | "escodegen": "1.8.x", 395 | "esprima": "2.7.x", 396 | "glob": "^5.0.15", 397 | "handlebars": "^4.0.1", 398 | "js-yaml": "3.x", 399 | "mkdirp": "0.5.x", 400 | "nopt": "3.x", 401 | "once": "1.x", 402 | "resolve": "1.1.x", 403 | "supports-color": "^3.1.0", 404 | "which": "^1.1.1", 405 | "wordwrap": "^1.0.0" 406 | } 407 | }, 408 | "jmespath": { 409 | "version": "0.15.0", 410 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 411 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 412 | }, 413 | "js-yaml": { 414 | "version": "3.12.0", 415 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", 416 | "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", 417 | "dev": true, 418 | "requires": { 419 | "argparse": "^1.0.7", 420 | "esprima": "^4.0.0" 421 | }, 422 | "dependencies": { 423 | "esprima": { 424 | "version": "4.0.1", 425 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 426 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 427 | "dev": true 428 | } 429 | } 430 | }, 431 | "just-extend": { 432 | "version": "1.1.27", 433 | "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", 434 | "integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==", 435 | "dev": true 436 | }, 437 | "kind-of": { 438 | "version": "3.2.2", 439 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 440 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 441 | "dev": true, 442 | "optional": true, 443 | "requires": { 444 | "is-buffer": "^1.1.5" 445 | } 446 | }, 447 | "lazy-cache": { 448 | "version": "1.0.4", 449 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 450 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", 451 | "dev": true, 452 | "optional": true 453 | }, 454 | "levn": { 455 | "version": "0.3.0", 456 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 457 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 458 | "dev": true, 459 | "requires": { 460 | "prelude-ls": "~1.1.2", 461 | "type-check": "~0.3.2" 462 | } 463 | }, 464 | "lodash.get": { 465 | "version": "4.4.2", 466 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", 467 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", 468 | "dev": true 469 | }, 470 | "lodash.merge": { 471 | "version": "4.6.1", 472 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", 473 | "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" 474 | }, 475 | "lolex": { 476 | "version": "2.7.1", 477 | "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.1.tgz", 478 | "integrity": "sha512-Oo2Si3RMKV3+lV5MsSWplDQFoTClz/24S0MMHYcgGWWmFXr6TMlqcqk/l1GtH+d5wLBwNRiqGnwDRMirtFalJw==", 479 | "dev": true 480 | }, 481 | "longest": { 482 | "version": "1.0.1", 483 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 484 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", 485 | "dev": true, 486 | "optional": true 487 | }, 488 | "minimatch": { 489 | "version": "3.0.4", 490 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 491 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 492 | "dev": true, 493 | "requires": { 494 | "brace-expansion": "^1.1.7" 495 | } 496 | }, 497 | "minimist": { 498 | "version": "0.0.10", 499 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 500 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", 501 | "dev": true 502 | }, 503 | "mkdirp": { 504 | "version": "0.5.1", 505 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 506 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 507 | "dev": true, 508 | "requires": { 509 | "minimist": "0.0.8" 510 | }, 511 | "dependencies": { 512 | "minimist": { 513 | "version": "0.0.8", 514 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 515 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 516 | "dev": true 517 | } 518 | } 519 | }, 520 | "mocha": { 521 | "version": "5.2.0", 522 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 523 | "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", 524 | "dev": true, 525 | "requires": { 526 | "browser-stdout": "1.3.1", 527 | "commander": "2.15.1", 528 | "debug": "3.1.0", 529 | "diff": "3.5.0", 530 | "escape-string-regexp": "1.0.5", 531 | "glob": "7.1.2", 532 | "growl": "1.10.5", 533 | "he": "1.1.1", 534 | "minimatch": "3.0.4", 535 | "mkdirp": "0.5.1", 536 | "supports-color": "5.4.0" 537 | }, 538 | "dependencies": { 539 | "glob": { 540 | "version": "7.1.2", 541 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 542 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 543 | "dev": true, 544 | "requires": { 545 | "fs.realpath": "^1.0.0", 546 | "inflight": "^1.0.4", 547 | "inherits": "2", 548 | "minimatch": "^3.0.4", 549 | "once": "^1.3.0", 550 | "path-is-absolute": "^1.0.0" 551 | } 552 | }, 553 | "has-flag": { 554 | "version": "3.0.0", 555 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 556 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 557 | "dev": true 558 | }, 559 | "supports-color": { 560 | "version": "5.4.0", 561 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 562 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 563 | "dev": true, 564 | "requires": { 565 | "has-flag": "^3.0.0" 566 | } 567 | } 568 | } 569 | }, 570 | "ms": { 571 | "version": "2.0.0", 572 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 573 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 574 | "dev": true 575 | }, 576 | "nise": { 577 | "version": "1.4.2", 578 | "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.2.tgz", 579 | "integrity": "sha512-BxH/DxoQYYdhKgVAfqVy4pzXRZELHOIewzoesxpjYvpU+7YOalQhGNPf7wAx8pLrTNPrHRDlLOkAl8UI0ZpXjw==", 580 | "dev": true, 581 | "requires": { 582 | "@sinonjs/formatio": "^2.0.0", 583 | "just-extend": "^1.1.27", 584 | "lolex": "^2.3.2", 585 | "path-to-regexp": "^1.7.0", 586 | "text-encoding": "^0.6.4" 587 | } 588 | }, 589 | "nopt": { 590 | "version": "3.0.6", 591 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", 592 | "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", 593 | "dev": true, 594 | "requires": { 595 | "abbrev": "1" 596 | } 597 | }, 598 | "once": { 599 | "version": "1.4.0", 600 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 601 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 602 | "dev": true, 603 | "requires": { 604 | "wrappy": "1" 605 | } 606 | }, 607 | "optimist": { 608 | "version": "0.6.1", 609 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 610 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 611 | "dev": true, 612 | "requires": { 613 | "minimist": "~0.0.1", 614 | "wordwrap": "~0.0.2" 615 | }, 616 | "dependencies": { 617 | "wordwrap": { 618 | "version": "0.0.3", 619 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 620 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", 621 | "dev": true 622 | } 623 | } 624 | }, 625 | "optionator": { 626 | "version": "0.8.2", 627 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 628 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 629 | "dev": true, 630 | "requires": { 631 | "deep-is": "~0.1.3", 632 | "fast-levenshtein": "~2.0.4", 633 | "levn": "~0.3.0", 634 | "prelude-ls": "~1.1.2", 635 | "type-check": "~0.3.2", 636 | "wordwrap": "~1.0.0" 637 | } 638 | }, 639 | "path-is-absolute": { 640 | "version": "1.0.1", 641 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 642 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 643 | "dev": true 644 | }, 645 | "path-to-regexp": { 646 | "version": "1.7.0", 647 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", 648 | "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", 649 | "dev": true, 650 | "requires": { 651 | "isarray": "0.0.1" 652 | }, 653 | "dependencies": { 654 | "isarray": { 655 | "version": "0.0.1", 656 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 657 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 658 | "dev": true 659 | } 660 | } 661 | }, 662 | "pathval": { 663 | "version": "1.1.0", 664 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 665 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 666 | "dev": true 667 | }, 668 | "prelude-ls": { 669 | "version": "1.1.2", 670 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 671 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 672 | "dev": true 673 | }, 674 | "punycode": { 675 | "version": "1.3.2", 676 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 677 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 678 | }, 679 | "querystring": { 680 | "version": "0.2.0", 681 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 682 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 683 | }, 684 | "repeat-string": { 685 | "version": "1.6.1", 686 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 687 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", 688 | "dev": true, 689 | "optional": true 690 | }, 691 | "resolve": { 692 | "version": "1.1.7", 693 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", 694 | "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", 695 | "dev": true 696 | }, 697 | "retry": { 698 | "version": "0.12.0", 699 | "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", 700 | "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" 701 | }, 702 | "right-align": { 703 | "version": "0.1.3", 704 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 705 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 706 | "dev": true, 707 | "optional": true, 708 | "requires": { 709 | "align-text": "^0.1.1" 710 | } 711 | }, 712 | "samsam": { 713 | "version": "1.3.0", 714 | "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", 715 | "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", 716 | "dev": true 717 | }, 718 | "sax": { 719 | "version": "1.2.1", 720 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 721 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 722 | }, 723 | "sinon": { 724 | "version": "6.1.4", 725 | "resolved": "https://registry.npmjs.org/sinon/-/sinon-6.1.4.tgz", 726 | "integrity": "sha512-NFEts+4D4jp2sBjL94fQpZk5o73kzn/g58+I9Dp15i9vsnT4Lk1UEyUf2jACODWLG6Pz/llF0sArYUw47Aarmg==", 727 | "dev": true, 728 | "requires": { 729 | "@sinonjs/formatio": "^2.0.0", 730 | "@sinonjs/samsam": "^2.0.0", 731 | "diff": "^3.5.0", 732 | "lodash.get": "^4.4.2", 733 | "lolex": "^2.7.1", 734 | "nise": "^1.4.2", 735 | "supports-color": "^5.4.0", 736 | "type-detect": "^4.0.8" 737 | }, 738 | "dependencies": { 739 | "has-flag": { 740 | "version": "3.0.0", 741 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 742 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 743 | "dev": true 744 | }, 745 | "supports-color": { 746 | "version": "5.4.0", 747 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 748 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 749 | "dev": true, 750 | "requires": { 751 | "has-flag": "^3.0.0" 752 | } 753 | } 754 | } 755 | }, 756 | "source-map": { 757 | "version": "0.2.0", 758 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", 759 | "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", 760 | "dev": true, 761 | "optional": true, 762 | "requires": { 763 | "amdefine": ">=0.0.4" 764 | } 765 | }, 766 | "sprintf-js": { 767 | "version": "1.0.3", 768 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 769 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 770 | "dev": true 771 | }, 772 | "supports-color": { 773 | "version": "3.2.3", 774 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", 775 | "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", 776 | "dev": true, 777 | "requires": { 778 | "has-flag": "^1.0.0" 779 | } 780 | }, 781 | "text-encoding": { 782 | "version": "0.6.4", 783 | "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", 784 | "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", 785 | "dev": true 786 | }, 787 | "type-check": { 788 | "version": "0.3.2", 789 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 790 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 791 | "dev": true, 792 | "requires": { 793 | "prelude-ls": "~1.1.2" 794 | } 795 | }, 796 | "type-detect": { 797 | "version": "4.0.8", 798 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 799 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 800 | "dev": true 801 | }, 802 | "uglify-js": { 803 | "version": "2.8.29", 804 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", 805 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 806 | "dev": true, 807 | "optional": true, 808 | "requires": { 809 | "source-map": "~0.5.1", 810 | "uglify-to-browserify": "~1.0.0", 811 | "yargs": "~3.10.0" 812 | }, 813 | "dependencies": { 814 | "source-map": { 815 | "version": "0.5.7", 816 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 817 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 818 | "dev": true, 819 | "optional": true 820 | } 821 | } 822 | }, 823 | "uglify-to-browserify": { 824 | "version": "1.0.2", 825 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 826 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 827 | "dev": true, 828 | "optional": true 829 | }, 830 | "url": { 831 | "version": "0.10.3", 832 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 833 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 834 | "requires": { 835 | "punycode": "1.3.2", 836 | "querystring": "0.2.0" 837 | } 838 | }, 839 | "uuid": { 840 | "version": "3.3.2", 841 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 842 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 843 | }, 844 | "which": { 845 | "version": "1.3.1", 846 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 847 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 848 | "dev": true, 849 | "requires": { 850 | "isexe": "^2.0.0" 851 | } 852 | }, 853 | "window-size": { 854 | "version": "0.1.0", 855 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 856 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", 857 | "dev": true, 858 | "optional": true 859 | }, 860 | "wordwrap": { 861 | "version": "1.0.0", 862 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 863 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 864 | "dev": true 865 | }, 866 | "wrappy": { 867 | "version": "1.0.2", 868 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 869 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 870 | "dev": true 871 | }, 872 | "xml2js": { 873 | "version": "0.4.19", 874 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 875 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 876 | "requires": { 877 | "sax": ">=0.6.0", 878 | "xmlbuilder": "~9.0.1" 879 | } 880 | }, 881 | "xmlbuilder": { 882 | "version": "9.0.7", 883 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 884 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 885 | }, 886 | "yargs": { 887 | "version": "3.10.0", 888 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 889 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 890 | "dev": true, 891 | "optional": true, 892 | "requires": { 893 | "camelcase": "^1.0.2", 894 | "cliui": "^2.1.0", 895 | "decamelize": "^1.0.0", 896 | "window-size": "0.1.0" 897 | } 898 | } 899 | } 900 | } 901 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-kinesis-writable", 3 | "description": "A stream implementation for kinesis.", 4 | "version": "4.3.0", 5 | "author": "José F. Romaniello (http://joseoncode.com)", 6 | "license": "MIT", 7 | "repository": { 8 | "url": "git://github.com/auth0/kinesis-writable.git" 9 | }, 10 | "main": "index.js", 11 | "scripts": { 12 | "test": "NODE_ENV=test mocha -R spec --timeout 5000", 13 | "cover": "NODE_ENV=test istanbul cover _mocha -- -R spec --timeout 5000" 14 | }, 15 | "dependencies": { 16 | "aws-sdk": "^2.610.0", 17 | "fast-safe-stringify": "^2.0.6", 18 | "lodash.merge": "^4.6.1", 19 | "retry": "^0.12.0" 20 | }, 21 | "devDependencies": { 22 | "chai": "^4.1.0", 23 | "istanbul": "^0.4.5", 24 | "mocha": "^5.2.0", 25 | "sinon": "^6.1.4" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | const sinon = require('sinon'); 4 | const assert = require('assert'); 5 | const KinesisStream = require('../'); 6 | const expect = require('chai').expect; 7 | 8 | describe('KinesisStream', function() { 9 | describe('#constructor', function() { 10 | it('should throw if .streamName is not provided', function() { 11 | expect(function() {new KinesisStream({});}).to.throw(assert.AssertionError, /streamName/); 12 | }); 13 | it('should build a stream with default configurations', function() { 14 | const ks = new KinesisStream({ 15 | streamName: 'test', 16 | kinesis: {} 17 | }); 18 | 19 | expect(ks.hasPriority).to.be.a('function'); 20 | expect(ks.recordsQueue).to.exist; 21 | expect(ks.partitionKey).to.be.string; 22 | }); 23 | it('should build a stream with configured endpoint and objectMode', function() { 24 | const ks = new KinesisStream({ 25 | streamName: 'test', 26 | objectMode: true, 27 | kinesis: { 28 | endpoint: 'http://somehost:1234' 29 | } 30 | }); 31 | 32 | expect(ks.hasPriority).to.be.a('function'); 33 | expect(ks.recordsQueue).to.exist; 34 | expect(ks.partitionKey).to.be.a('function'); 35 | expect(ks.kinesis.endpoint).to.equal('http://somehost:1234'); 36 | expect(ks._writableState.objectMode).to.equal(true); 37 | }); 38 | }); 39 | describe('#_write', function() { 40 | var ks; 41 | const message = {test: true}; 42 | beforeEach(function() { 43 | ks = new KinesisStream({ 44 | streamName: 'test', 45 | kinesis: {} 46 | }); 47 | ks.dispatch = sinon.spy(); 48 | }); 49 | it('should call immediately dispatch with a single message if it has priority', function() { 50 | ks.hasPriority = function() { 51 | return true; 52 | }; 53 | ks.write(new Buffer(JSON.stringify(message))); 54 | expect(ks.dispatch.calledOnce).to.be.true; 55 | expect(ks.dispatch.calledWith([message])).to.be.true; 56 | }); 57 | it('should add message to queue if no priority was specified and add a timer', function() { 58 | ks.write(new Buffer(JSON.stringify(message))); 59 | expect(ks.dispatch.calledOnce).to.be.false; 60 | expect(ks.timer).to.exist; 61 | expect(ks.recordsQueue.length).to.equal(1); 62 | }); 63 | it('should add message to queue and call flush is size has crossed the threshold', function() { 64 | for (var i = 0; i < 10; i++) { 65 | ks.write(new Buffer(JSON.stringify(message))); 66 | } 67 | expect(ks.dispatch.calledOnce).to.be.true; 68 | expect(ks.dispatch.calledWith([message, message, message, message, message, message, message, message, message, message])).to.be.true; 69 | }); 70 | it('should call #dispatch once timer hits', function(done) { 71 | this.timeout(3000); 72 | ks.buffer.timeout = 1; 73 | // write one message to start timeout 74 | ks.write(new Buffer(JSON.stringify(message))); 75 | // write another after 500ms. 76 | setTimeout(() => ks.write(new Buffer(JSON.stringify(message))), 500); 77 | // write another after 750ms. 78 | setTimeout(() => ks.write(new Buffer(JSON.stringify(message))), 750); 79 | // at 1100 ms timer should have fired still. 80 | setTimeout(function() { 81 | expect(ks.dispatch.calledOnce).to.be.true; 82 | expect(ks.dispatch.calledWith([message, message, message])).to.be.true; 83 | }, 1100); 84 | // and at 2 seconds it should not have fired again ... 85 | setTimeout(function() { 86 | expect(ks.dispatch.calledOnce).to.be.true; 87 | expect(ks.dispatch.calledWith([message, message, message])).to.be.true; 88 | done(); 89 | }, 2000); 90 | }); 91 | }); 92 | describe('#flush', function() { 93 | it('should call #dispatch with at most buffer size', function() { 94 | const ks = new KinesisStream({ 95 | streamName: 'test', 96 | kinesis: {} 97 | }); 98 | ks.dispatch = sinon.spy(); 99 | ks.recordsQueue = [1,2,3]; 100 | ks.flush(); 101 | expect(ks.dispatch.calledOnce).to.be.true; 102 | expect(ks.dispatch.calledWith([1,2,3])).to.be.true; 103 | }); 104 | }); 105 | describe('#dispatch', function() { 106 | var ks, kinesis = {}; 107 | beforeEach(function() { 108 | ks = new KinesisStream({ 109 | streamName: 'test', 110 | kinesis: kinesis 111 | }); 112 | }); 113 | it('should return immediately if no messages are provided', function(done) { 114 | kinesis.putRecords = sinon.spy(); 115 | ks.dispatch([], function() { 116 | expect(kinesis.putRecords.calledOnce).to.be.false; 117 | done(); 118 | }); 119 | }); 120 | it('should retry if #putRecords failed', function(done) { 121 | const message = {test: true}; 122 | const stub = sinon.stub(ks, 'putRecords').callsFake(function(r, cb) { 123 | return cb(new Error()); 124 | }); 125 | 126 | ks.on('error', function(err) { 127 | expect(err).to.exist; 128 | expect(err.records.length).to.equal(2); 129 | expect(stub.calledThrice).to.be.true; 130 | }); 131 | 132 | ks.dispatch([message, message], function(err) { 133 | expect(err).to.exist; 134 | done(); 135 | }); 136 | }); 137 | 138 | it('should putRecords without error when record contains circular references', function() { 139 | const logMessage = (s) => { return s.getCall(0).args[0][0].Data; }; 140 | ks.putRecords = sinon.spy(); 141 | 142 | const message = { 143 | hi: 'hello' 144 | }; 145 | message.message = message; 146 | 147 | ks.dispatch([message]); 148 | 149 | expect(ks.putRecords.calledOnce).to.be.true; 150 | expect('{"hi":"hello","message":"[Circular]"}').to.equal(logMessage(ks.putRecords)); 151 | }); 152 | }); 153 | }); 154 | --------------------------------------------------------------------------------