├── .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 |
--------------------------------------------------------------------------------