You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by nk...@apache.org on 2019/06/13 06:28:37 UTC

[pulsar-client-node] branch master updated (3fa417f -> 006871d)

This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git.


    from 3fa417f  Merge pull request #34 from shustsud/Add_subscription_type_to_examples
     new 0f9254e  move libraries in perf to devDependencies
     new 4ed9ce0  update package-lock
     new 006871d  Merge pull request #35 from k2la/lighten_client

The 36 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .eslintignore     |  1 +
 package-lock.json | 24 +++++++++++++++---------
 package.json      | 10 +++++-----
 3 files changed, 21 insertions(+), 14 deletions(-)
 create mode 100644 .eslintignore


[pulsar-client-node] 10/36: Support authentication

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 0dccd84811597258a559f137cf45593f7734f08f
Author: Masahiro Sakamoto <ma...@yahoo-corp.jp>
AuthorDate: Mon Mar 25 16:21:16 2019 +0900

    Support authentication
---
 binding.gyp                               |  3 +-
 index.js => examples/consumer_tls_auth.js | 36 +++++++++---
 index.js => examples/producer_tls_auth.js | 38 ++++++++++---
 index.js                                  |  6 ++
 src/Authentication.cc                     | 95 +++++++++++++++++++++++++++++++
 src/{addon.cc => Authentication.h}        | 29 +++++-----
 index.js => src/AuthenticationAthenz.js   | 13 +++--
 index.js => src/AuthenticationTls.js      | 12 ++--
 index.js => src/AuthenticationToken.js    | 12 ++--
 src/Client.cc                             | 11 ++++
 src/addon.cc                              |  6 +-
 11 files changed, 213 insertions(+), 48 deletions(-)

diff --git a/binding.gyp b/binding.gyp
index 88a6c70..cfcf8b7 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -29,12 +29,13 @@
       "sources": [
         "src/addon.cc",
         "src/Message.cc",
+        "src/MessageId.cc",
+        "src/Authentication.cc",
         "src/Client.cc",
         "src/Producer.cc",
         "src/ProducerConfig.cc",
         "src/Consumer.cc",
         "src/ConsumerConfig.cc",
-        "src/MessageId.cc",
       ],
       "libraries": ["-lpulsar"],
     }
diff --git a/index.js b/examples/consumer_tls_auth.js
similarity index 51%
copy from index.js
copy to examples/consumer_tls_auth.js
index 03f3a08..e71b251 100644
--- a/index.js
+++ b/examples/consumer_tls_auth.js
@@ -17,12 +17,34 @@
  * under the License.
  */
 
-const PulsarBinding = require('bindings')('Pulsar');
+const Pulsar = require('../index.js');
 
-const Pulsar = {
-  Client: PulsarBinding.Client,
-  Message: PulsarBinding.Message,
-  MessageId: PulsarBinding.MessageId,
-};
+(async () => {
+  const auth = new Pulsar.AuthenticationTls({
+    certificatePath: '/path/to/client.crt',
+    privateKeyPath: '/path/to/client.key',
+  });
 
-module.exports = Pulsar;
+  // Create a client
+  const client = new Pulsar.Client({
+    serviceUrl: 'pulsar+ssl://localhost:6651',
+    authentication: auth,
+    tlsTrustCertsFilePath: '/path/to/server.crt',
+  });
+
+  // Create a consumer
+  const consumer = await client.subscribe({
+    topic: 'persistent://public/default/my-topic',
+    subscription: 'sub1',
+  });
+
+  // Receive messages
+  for (let i = 0; i < 10; i += 1) {
+    const msg = await consumer.receive();
+    console.log(msg.getData().toString());
+    consumer.acknowledge(msg);
+  }
+
+  await consumer.close();
+  await client.close();
+})();
diff --git a/index.js b/examples/producer_tls_auth.js
similarity index 50%
copy from index.js
copy to examples/producer_tls_auth.js
index 03f3a08..df4e33e 100644
--- a/index.js
+++ b/examples/producer_tls_auth.js
@@ -17,12 +17,36 @@
  * under the License.
  */
 
-const PulsarBinding = require('bindings')('Pulsar');
+const Pulsar = require('../index.js');
 
-const Pulsar = {
-  Client: PulsarBinding.Client,
-  Message: PulsarBinding.Message,
-  MessageId: PulsarBinding.MessageId,
-};
+(async () => {
+  const auth = new Pulsar.AuthenticationTls({
+    certificatePath: '/path/to/client.crt',
+    privateKeyPath: '/path/to/client.key',
+  });
 
-module.exports = Pulsar;
+  // Create a client
+  const client = new Pulsar.Client({
+    serviceUrl: 'pulsar+ssl://localhost:6651',
+    authentication: auth,
+    tlsTrustCertsFilePath: '/path/to/server.crt',
+  });
+
+  // Create a producer
+  const producer = await client.createProducer({
+    topic: 'persistent://public/default/my-topic',
+  });
+
+  // Send messages
+  for (let i = 0; i < 10; i += 1) {
+    const msg = `my-message-${i}`;
+    producer.send({
+      data: Buffer.from(msg),
+    });
+    console.log(`Sent message: ${msg}`);
+  }
+  await producer.flush();
+
+  await producer.close();
+  await client.close();
+})();
diff --git a/index.js b/index.js
index 03f3a08..97c83c6 100644
--- a/index.js
+++ b/index.js
@@ -18,11 +18,17 @@
  */
 
 const PulsarBinding = require('bindings')('Pulsar');
+const AuthenticationTls = require('./src/AuthenticationTls.js');
+const AuthenticationAthenz = require('./src/AuthenticationAthenz.js');
+const AuthenticationToken = require('./src/AuthenticationToken.js');
 
 const Pulsar = {
   Client: PulsarBinding.Client,
   Message: PulsarBinding.Message,
   MessageId: PulsarBinding.MessageId,
+  AuthenticationTls,
+  AuthenticationAthenz,
+  AuthenticationToken,
 };
 
 module.exports = Pulsar;
diff --git a/src/Authentication.cc b/src/Authentication.cc
new file mode 100644
index 0000000..226fd0a
--- /dev/null
+++ b/src/Authentication.cc
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "Authentication.h"
+
+static const std::string PARAM_TLS_CERT = "certificatePath";
+static const std::string PARAM_TLS_KEY = "privateKeyPath";
+static const std::string PARAM_TOKEN = "token";
+
+Napi::FunctionReference Authentication::constructor;
+
+Napi::Object Authentication::Init(Napi::Env env, Napi::Object exports) {
+  Napi::HandleScope scope(env);
+
+  Napi::Function func = DefineClass(env, "Authentication", {});
+
+  constructor = Napi::Persistent(func);
+  constructor.SuppressDestruct();
+
+  exports.Set("Authentication", func);
+  return exports;
+}
+
+Authentication::Authentication(const Napi::CallbackInfo &info)
+    : Napi::ObjectWrap<Authentication>(info), cAuthentication(nullptr) {
+  Napi::Env env = info.Env();
+  Napi::HandleScope scope(env);
+
+  if (info.Length() < 1 || !info[0].IsString() || info[0].ToString().Utf8Value().empty()) {
+    Napi::Error::New(env, "Authentication method is not specified").ThrowAsJavaScriptException();
+    return;
+  }
+
+  std::string authMethod = info[0].ToString().Utf8Value();
+
+  if (authMethod == "tls" || authMethod == "token") {
+    if (info.Length() < 2 || !info[1].IsObject()) {
+      Napi::Error::New(env, "Authentication parameter must be a object").ThrowAsJavaScriptException();
+      return;
+    }
+
+    Napi::Object obj = info[1].ToObject();
+
+    if (authMethod == "tls") {
+      if (!obj.Has(PARAM_TLS_CERT) || !obj.Get(PARAM_TLS_CERT).IsString() || !obj.Has(PARAM_TLS_KEY) ||
+          !obj.Get(PARAM_TLS_KEY).IsString()) {
+        Napi::Error::New(env, "Missing required parameter").ThrowAsJavaScriptException();
+        return;
+      }
+      this->cAuthentication =
+          pulsar_authentication_tls_create(obj.Get(PARAM_TLS_CERT).ToString().Utf8Value().c_str(),
+                                           obj.Get(PARAM_TLS_KEY).ToString().Utf8Value().c_str());
+    } else if (authMethod == "token") {
+      if (!obj.Has(PARAM_TOKEN) || !obj.Get(PARAM_TOKEN).IsString()) {
+        Napi::Error::New(env, "Missing required parameter").ThrowAsJavaScriptException();
+        return;
+      }
+      this->cAuthentication =
+          pulsar_authentication_token_create(obj.Get(PARAM_TOKEN).ToString().Utf8Value().c_str());
+    }
+  } else if (authMethod == "athenz") {
+    if (info.Length() < 2 || !info[1].IsString()) {
+      Napi::Error::New(env, "Authentication parameter must be a JSON string").ThrowAsJavaScriptException();
+      return;
+    }
+    this->cAuthentication = pulsar_authentication_athenz_create(info[1].ToString().Utf8Value().c_str());
+  } else {
+    Napi::Error::New(env, "Unsupported authentication method").ThrowAsJavaScriptException();
+    return;
+  }
+}
+
+Authentication::~Authentication() {
+  if (this->cAuthentication != nullptr) {
+    pulsar_authentication_free(this->cAuthentication);
+  }
+}
+
+pulsar_authentication_t *Authentication::GetCAuthentication() { return this->cAuthentication; }
diff --git a/src/addon.cc b/src/Authentication.h
similarity index 65%
copy from src/addon.cc
copy to src/Authentication.h
index 9e75d3f..3666bd8 100644
--- a/src/addon.cc
+++ b/src/Authentication.h
@@ -17,19 +17,22 @@
  * under the License.
  */
 
-#include "Client.h"
-#include "Consumer.h"
-#include "Message.h"
-#include "MessageId.h"
-#include "Producer.h"
+#ifndef AUTH_H
+#define AUTH_H
+
 #include <napi.h>
+#include <pulsar/c/authentication.h>
+
+class Authentication : public Napi::ObjectWrap<Authentication> {
+ public:
+  static Napi::Object Init(Napi::Env env, Napi::Object exports);
+  Authentication(const Napi::CallbackInfo &info);
+  ~Authentication();
+  pulsar_authentication_t *GetCAuthentication();
 
-Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
-  Message::Init(env, exports);
-  MessageId::Init(env, exports);
-  Producer::Init(env, exports);
-  Consumer::Init(env, exports);
-  return Client::Init(env, exports);
-}
+ private:
+  static Napi::FunctionReference constructor;
+  pulsar_authentication_t *cAuthentication;
+};
 
-NODE_API_MODULE(NODE_GYP_MODULE_NAME, InitAll)
+#endif
diff --git a/index.js b/src/AuthenticationAthenz.js
similarity index 77%
copy from index.js
copy to src/AuthenticationAthenz.js
index 03f3a08..edbce3b 100644
--- a/index.js
+++ b/src/AuthenticationAthenz.js
@@ -19,10 +19,11 @@
 
 const PulsarBinding = require('bindings')('Pulsar');
 
-const Pulsar = {
-  Client: PulsarBinding.Client,
-  Message: PulsarBinding.Message,
-  MessageId: PulsarBinding.MessageId,
-};
+class AuthenticationAthenz {
+  constructor(params) {
+    const paramsStr = (typeof params === 'object') ? JSON.stringify(params) : params;
+    this.binding = new PulsarBinding.Authentication('athenz', paramsStr);
+  }
+}
 
-module.exports = Pulsar;
+module.exports = AuthenticationAthenz;
diff --git a/index.js b/src/AuthenticationTls.js
similarity index 84%
copy from index.js
copy to src/AuthenticationTls.js
index 03f3a08..f00b579 100644
--- a/index.js
+++ b/src/AuthenticationTls.js
@@ -19,10 +19,10 @@
 
 const PulsarBinding = require('bindings')('Pulsar');
 
-const Pulsar = {
-  Client: PulsarBinding.Client,
-  Message: PulsarBinding.Message,
-  MessageId: PulsarBinding.MessageId,
-};
+class AuthenticationTls {
+  constructor(params) {
+    this.binding = new PulsarBinding.Authentication('tls', params);
+  }
+}
 
-module.exports = Pulsar;
+module.exports = AuthenticationTls;
diff --git a/index.js b/src/AuthenticationToken.js
similarity index 83%
copy from index.js
copy to src/AuthenticationToken.js
index 03f3a08..e40c892 100644
--- a/index.js
+++ b/src/AuthenticationToken.js
@@ -19,10 +19,10 @@
 
 const PulsarBinding = require('bindings')('Pulsar');
 
-const Pulsar = {
-  Client: PulsarBinding.Client,
-  Message: PulsarBinding.Message,
-  MessageId: PulsarBinding.MessageId,
-};
+class AuthenticationToken {
+  constructor(params) {
+    this.binding = new PulsarBinding.Authentication('token', params);
+  }
+}
 
-module.exports = Pulsar;
+module.exports = AuthenticationToken;
diff --git a/src/Client.cc b/src/Client.cc
index 16fb13d..123fc0f 100644
--- a/src/Client.cc
+++ b/src/Client.cc
@@ -20,11 +20,14 @@
 #include "Client.h"
 #include "Consumer.h"
 #include "Producer.h"
+#include "Authentication.h"
 #include <pulsar/c/client.h>
 #include <pulsar/c/client_configuration.h>
 #include <pulsar/c/result.h>
 
 static const std::string CFG_SERVICE_URL = "serviceUrl";
+static const std::string CFG_AUTH = "authentication";
+static const std::string CFG_AUTH_PROP = "binding";
 static const std::string CFG_OP_TIMEOUT = "operationTimeoutSeconds";
 static const std::string CFG_IO_THREADS = "ioThreads";
 static const std::string CFG_LISTENER_THREADS = "messageListenerThreads";
@@ -68,6 +71,14 @@ Client::Client(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Client>(info)
 
   pulsar_client_configuration_t *cClientConfig = pulsar_client_configuration_create();
 
+  if (clientConfig.Has(CFG_AUTH) && clientConfig.Get(CFG_AUTH).IsObject()) {
+    Napi::Object obj = clientConfig.Get(CFG_AUTH).ToObject();
+    if (obj.Has(CFG_AUTH_PROP) && obj.Get(CFG_AUTH_PROP).IsObject()) {
+      Authentication *auth = Authentication::Unwrap(obj.Get(CFG_AUTH_PROP).ToObject());
+      pulsar_client_configuration_set_auth(cClientConfig, auth->GetCAuthentication());
+    }
+  }
+
   if (clientConfig.Has(CFG_OP_TIMEOUT) && clientConfig.Get(CFG_OP_TIMEOUT).IsNumber()) {
     int32_t operationTimeoutSeconds = clientConfig.Get(CFG_OP_TIMEOUT).ToNumber().Int32Value();
     if (operationTimeoutSeconds > 0) {
diff --git a/src/addon.cc b/src/addon.cc
index 9e75d3f..050ae12 100644
--- a/src/addon.cc
+++ b/src/addon.cc
@@ -17,16 +17,18 @@
  * under the License.
  */
 
-#include "Client.h"
-#include "Consumer.h"
 #include "Message.h"
 #include "MessageId.h"
+#include "Authentication.h"
 #include "Producer.h"
+#include "Consumer.h"
+#include "Client.h"
 #include <napi.h>
 
 Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
   Message::Init(env, exports);
   MessageId::Init(env, exports);
+  Authentication::Init(env, exports);
   Producer::Init(env, exports);
   Consumer::Init(env, exports);
   return Client::Init(env, exports);


[pulsar-client-node] 17/36: Upgrade js-yaml to fix security vulnerability

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 869a5e9777499a38fc312023b614267d4e157fee
Author: hrsakai <hs...@yahoo-corp.jp>
AuthorDate: Tue Apr 23 10:28:29 2019 +0900

    Upgrade js-yaml to fix security vulnerability
---
 package-lock.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 7e530be..b307ed5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4026,9 +4026,9 @@
       "dev": true
     },
     "js-yaml": {
-      "version": "3.13.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz",
-      "integrity": "sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==",
+      "version": "3.13.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+      "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
       "dev": true,
       "requires": {
         "argparse": "^1.0.7",


[pulsar-client-node] 25/36: Merge ReceiveWithTimeout into Receive (#30)

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 3561f2b2cff566e974bf1a20817c60f1cece026e
Author: hrsakai <hs...@yahoo-corp.jp>
AuthorDate: Tue Jun 11 14:39:13 2019 +0900

    Merge ReceiveWithTimeout into Receive (#30)
    
    Change Interface to same as java / cpp client.
---
 src/Consumer.cc          | 19 ++++++++-----------
 src/Consumer.h           |  1 -
 tests/end_to_end.test.js |  2 +-
 3 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/src/Consumer.cc b/src/Consumer.cc
index 454ba3c..12bba8e 100644
--- a/src/Consumer.cc
+++ b/src/Consumer.cc
@@ -32,7 +32,6 @@ void Consumer::Init(Napi::Env env, Napi::Object exports) {
       DefineClass(env, "Consumer",
                   {
                       InstanceMethod("receive", &Consumer::Receive),
-                      InstanceMethod("receiveWithTimeout", &Consumer::ReceiveWithTimeout),
                       InstanceMethod("acknowledge", &Consumer::Acknowledge),
                       InstanceMethod("acknowledgeId", &Consumer::AcknowledgeId),
                       InstanceMethod("acknowledgeCumulative", &Consumer::AcknowledgeCumulative),
@@ -146,16 +145,14 @@ class ConsumerReceiveWorker : public Napi::AsyncWorker {
 
 Napi::Value Consumer::Receive(const Napi::CallbackInfo &info) {
   Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
-  ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->cConsumer);
-  wk->Queue();
-  return deferred.Promise();
-}
-
-Napi::Value Consumer::ReceiveWithTimeout(const Napi::CallbackInfo &info) {
-  Napi::Number timeout = info[0].As<Napi::Object>().ToNumber();
-  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
-  ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->cConsumer, timeout.Int64Value());
-  wk->Queue();
+  if (info[0].IsUndefined()) {
+    ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->cConsumer);
+    wk->Queue();
+  } else {
+    Napi::Number timeout = info[0].As<Napi::Object>().ToNumber();
+    ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->cConsumer, timeout.Int64Value());
+    wk->Queue();
+  }
   return deferred.Promise();
 }
 
diff --git a/src/Consumer.h b/src/Consumer.h
index 5e8bf5b..7aa41d9 100644
--- a/src/Consumer.h
+++ b/src/Consumer.h
@@ -36,7 +36,6 @@ class Consumer : public Napi::ObjectWrap<Consumer> {
   pulsar_consumer_t *cConsumer;
 
   Napi::Value Receive(const Napi::CallbackInfo &info);
-  Napi::Value ReceiveWithTimeout(const Napi::CallbackInfo &info);
   void Acknowledge(const Napi::CallbackInfo &info);
   void AcknowledgeId(const Napi::CallbackInfo &info);
   void AcknowledgeCumulative(const Napi::CallbackInfo &info);
diff --git a/tests/end_to_end.test.js b/tests/end_to_end.test.js
index ae24d86..97a6d6c 100644
--- a/tests/end_to_end.test.js
+++ b/tests/end_to_end.test.js
@@ -97,7 +97,7 @@ const Pulsar = require('../index.js');
         }
       }
 
-      await expect(consumer.receiveWithTimeout(1000)).rejects.toThrow('Failed to received message TimeOut');
+      await expect(consumer.receive(1000)).rejects.toThrow('Failed to received message TimeOut');
 
       await producer.close();
       await consumer.close();


[pulsar-client-node] 21/36: Change download url for pulsar pkg

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit d07afe5d5cc3f18755f443eb05f93176110e59c4
Author: hrsakai <hs...@yahoo-corp.jp>
AuthorDate: Fri Jun 7 15:19:27 2019 +0900

    Change download url for pulsar pkg
---
 pulsar-test-service-start.sh | 2 +-
 run-unit-tests.sh            | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/pulsar-test-service-start.sh b/pulsar-test-service-start.sh
index 78e715e..44cdf9c 100755
--- a/pulsar-test-service-start.sh
+++ b/pulsar-test-service-start.sh
@@ -28,7 +28,7 @@ PULSAR_DIR="${PULSAR_DIR:-/tmp/pulsar-test-dist}"
 PKG=apache-pulsar-${VERSION}-bin.tar.gz
 
 rm -rf $PULSAR_DIR
-curl -L --create-dir "https://www.apache.org/dyn/mirrors/mirrors.cgi?action=download&filename=pulsar/pulsar-${VERSION}/${PKG}" -o $PULSAR_DIR/$PKG
+curl -L --create-dir "https://archive.apache.org/dist/pulsar/pulsar-${VERSION}/${PKG}" -o $PULSAR_DIR/$PKG
 tar xfz $PULSAR_DIR/$PKG -C $PULSAR_DIR --strip-components 1
 
 DATA_DIR=/tmp/pulsar-test-data
diff --git a/run-unit-tests.sh b/run-unit-tests.sh
index 1acce0a..c521db4 100755
--- a/run-unit-tests.sh
+++ b/run-unit-tests.sh
@@ -28,7 +28,7 @@ VERSION="${VERSION:-`cat ./pulsar-version.txt`}"
 PULSAR_PKG_DIR="/tmp/pulsar-test-pkg"
 rm -rf $PULSAR_PKG_DIR
 for pkg in apache-pulsar-client-dev.deb apache-pulsar-client.deb;do
-  curl -L --create-dir "https://www.apache.org/dyn/mirrors/mirrors.cgi?action=download&filename=pulsar/pulsar-${VERSION}/DEB/${pkg}" -o $PULSAR_PKG_DIR/$pkg
+  curl -L --create-dir "https://archive.apache.org/dist/pulsar/pulsar-${VERSION}/DEB/${pkg}" -o $PULSAR_PKG_DIR/$pkg
 done;
 apt install $PULSAR_PKG_DIR/apache-pulsar-client*.deb
 


[pulsar-client-node] 22/36: Add acknowledgeCumulative / receiveWithTimeout methods

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit a945e49730ec4caf059dfb166020969dbcc69d6c
Author: hrsakai <hs...@yahoo-corp.jp>
AuthorDate: Fri Jun 7 15:21:03 2019 +0900

    Add acknowledgeCumulative / receiveWithTimeout methods
---
 src/Consumer.cc          | 52 ++++++++++++++++++++++++++++++++++++++----------
 src/Consumer.h           |  3 +++
 tests/end_to_end.test.js | 39 ++++++++++++++++++++++++++++++++++++
 3 files changed, 84 insertions(+), 10 deletions(-)

diff --git a/src/Consumer.cc b/src/Consumer.cc
index db6fab4..454ba3c 100644
--- a/src/Consumer.cc
+++ b/src/Consumer.cc
@@ -28,13 +28,17 @@ Napi::FunctionReference Consumer::constructor;
 void Consumer::Init(Napi::Env env, Napi::Object exports) {
   Napi::HandleScope scope(env);
 
-  Napi::Function func = DefineClass(env, "Consumer",
-                                    {
-                                        InstanceMethod("receive", &Consumer::Receive),
-                                        InstanceMethod("acknowledge", &Consumer::Acknowledge),
-                                        InstanceMethod("acknowledgeId", &Consumer::AcknowledgeId),
-                                        InstanceMethod("close", &Consumer::Close),
-                                    });
+  Napi::Function func =
+      DefineClass(env, "Consumer",
+                  {
+                      InstanceMethod("receive", &Consumer::Receive),
+                      InstanceMethod("receiveWithTimeout", &Consumer::ReceiveWithTimeout),
+                      InstanceMethod("acknowledge", &Consumer::Acknowledge),
+                      InstanceMethod("acknowledgeId", &Consumer::AcknowledgeId),
+                      InstanceMethod("acknowledgeCumulative", &Consumer::AcknowledgeCumulative),
+                      InstanceMethod("acknowledgeCumulativeId", &Consumer::AcknowledgeCumulativeId),
+                      InstanceMethod("close", &Consumer::Close),
+                  });
 
   constructor = Napi::Persistent(func);
   constructor.SuppressDestruct();
@@ -108,13 +112,20 @@ Napi::Value Consumer::NewInstance(const Napi::CallbackInfo &info, pulsar_client_
 
 class ConsumerReceiveWorker : public Napi::AsyncWorker {
  public:
-  ConsumerReceiveWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer)
+  ConsumerReceiveWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer,
+                        int64_t timeout = -1)
       : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
         deferred(deferred),
-        cConsumer(cConsumer) {}
+        cConsumer(cConsumer),
+        timeout(timeout) {}
   ~ConsumerReceiveWorker() {}
   void Execute() {
-    pulsar_result result = pulsar_consumer_receive(this->cConsumer, &(this->cMessage));
+    pulsar_result result;
+    if (timeout > 0) {
+      result = pulsar_consumer_receive_with_timeout(this->cConsumer, &(this->cMessage), timeout);
+    } else {
+      result = pulsar_consumer_receive(this->cConsumer, &(this->cMessage));
+    }
 
     if (result != pulsar_result_Ok) {
       SetError(std::string("Failed to received message ") + pulsar_result_str(result));
@@ -130,6 +141,7 @@ class ConsumerReceiveWorker : public Napi::AsyncWorker {
   Napi::Promise::Deferred deferred;
   pulsar_consumer_t *cConsumer;
   pulsar_message_t *cMessage;
+  int64_t timeout;
 };
 
 Napi::Value Consumer::Receive(const Napi::CallbackInfo &info) {
@@ -139,6 +151,14 @@ Napi::Value Consumer::Receive(const Napi::CallbackInfo &info) {
   return deferred.Promise();
 }
 
+Napi::Value Consumer::ReceiveWithTimeout(const Napi::CallbackInfo &info) {
+  Napi::Number timeout = info[0].As<Napi::Object>().ToNumber();
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->cConsumer, timeout.Int64Value());
+  wk->Queue();
+  return deferred.Promise();
+}
+
 void Consumer::Acknowledge(const Napi::CallbackInfo &info) {
   Napi::Object obj = info[0].As<Napi::Object>();
   Message *msg = Message::Unwrap(obj);
@@ -151,6 +171,18 @@ void Consumer::AcknowledgeId(const Napi::CallbackInfo &info) {
   pulsar_consumer_acknowledge_async_id(this->cConsumer, msgId->GetCMessageId(), NULL, NULL);
 }
 
+void Consumer::AcknowledgeCumulative(const Napi::CallbackInfo &info) {
+  Napi::Object obj = info[0].As<Napi::Object>();
+  Message *msg = Message::Unwrap(obj);
+  pulsar_consumer_acknowledge_cumulative_async(this->cConsumer, msg->GetCMessage(), NULL, NULL);
+}
+
+void Consumer::AcknowledgeCumulativeId(const Napi::CallbackInfo &info) {
+  Napi::Object obj = info[0].As<Napi::Object>();
+  MessageId *msgId = MessageId::Unwrap(obj);
+  pulsar_consumer_acknowledge_cumulative_async_id(this->cConsumer, msgId->GetCMessageId(), NULL, NULL);
+}
+
 class ConsumerCloseWorker : public Napi::AsyncWorker {
  public:
   ConsumerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer)
diff --git a/src/Consumer.h b/src/Consumer.h
index fe68124..5e8bf5b 100644
--- a/src/Consumer.h
+++ b/src/Consumer.h
@@ -36,8 +36,11 @@ class Consumer : public Napi::ObjectWrap<Consumer> {
   pulsar_consumer_t *cConsumer;
 
   Napi::Value Receive(const Napi::CallbackInfo &info);
+  Napi::Value ReceiveWithTimeout(const Napi::CallbackInfo &info);
   void Acknowledge(const Napi::CallbackInfo &info);
   void AcknowledgeId(const Napi::CallbackInfo &info);
+  void AcknowledgeCumulative(const Napi::CallbackInfo &info);
+  void AcknowledgeCumulativeId(const Napi::CallbackInfo &info);
   Napi::Value Close(const Napi::CallbackInfo &info);
 };
 
diff --git a/tests/end_to_end.test.js b/tests/end_to_end.test.js
index 9d5ed49..ae24d86 100644
--- a/tests/end_to_end.test.js
+++ b/tests/end_to_end.test.js
@@ -40,6 +40,7 @@ const Pulsar = require('../index.js');
         subscription: 'sub1',
         ackTimeoutMs: 10000,
       });
+
       expect(consumer).not.toBeNull();
 
       const messages = [];
@@ -62,6 +63,44 @@ const Pulsar = require('../index.js');
 
       await producer.close();
       await consumer.close();
+    });
+
+    test('acknowledgeCumulative', async () => {
+      const producer = await client.createProducer({
+        topic: 'persistent://public/default/acknowledgeCumulative',
+        sendTimeoutMs: 30000,
+        batchingEnabled: true,
+      });
+      expect(producer).not.toBeNull();
+
+      const consumer = await client.subscribe({
+        topic: 'persistent://public/default/acknowledgeCumulative',
+        subscription: 'sub1',
+        ackTimeoutMs: 10000,
+      });
+      expect(consumer).not.toBeNull();
+
+      const messages = [];
+      for (let i = 0; i < 10; i += 1) {
+        const msg = `my-message-${i}`;
+        producer.send({
+          data: Buffer.from(msg),
+        });
+        messages.push(msg);
+      }
+      await producer.flush();
+
+      for (let i = 0; i < 10; i += 1) {
+        const msg = await consumer.receive();
+        if (i === 9) {
+          consumer.acknowledgeCumulative(msg);
+        }
+      }
+
+      await expect(consumer.receiveWithTimeout(1000)).rejects.toThrow('Failed to received message TimeOut');
+
+      await producer.close();
+      await consumer.close();
       await client.close();
     });
   });


[pulsar-client-node] 35/36: update package-lock

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 4ed9ce0abd8b7217769a9f023998a79c541e3b31
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Thu Jun 13 13:13:40 2019 +0900

    update package-lock
---
 package-lock.json | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index b307ed5..fde0aeb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -829,7 +829,8 @@
     "base64-js": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
-      "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
+      "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
+      "dev": true
     },
     "bcrypt-pbkdf": {
       "version": "1.0.2",
@@ -1139,9 +1140,10 @@
       }
     },
     "commander": {
-      "version": "2.19.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
-      "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
+      "version": "2.20.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+      "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+      "dev": true
     },
     "compare-versions": {
       "version": "3.4.0",
@@ -1367,9 +1369,10 @@
       }
     },
     "delay": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/delay/-/delay-4.1.0.tgz",
-      "integrity": "sha512-8Hea6/aOu3bPdDBQhSRUEUzF0QwuWmSPuIK+sxNdvcJtSfzb6HXrTd9DFJBCJcV9o83fFECqTgllqdnmUfq9+w=="
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/delay/-/delay-4.3.0.tgz",
+      "integrity": "sha512-Lwaf3zVFDMBop1yDuFZ19F9WyGcZcGacsbdlZtWjQmM50tOcMntm1njF/Nb/Vjij3KaSvCF+sEYGKrrjObu2NA==",
+      "dev": true
     },
     "delayed-stream": {
       "version": "1.0.0",
@@ -3032,6 +3035,7 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-1.1.4.tgz",
       "integrity": "sha1-dBNAJdGuHP711/ESfrdWDeKgp3M=",
+      "dev": true,
       "requires": {
         "base64-js": "^1.2.0",
         "pako": "^1.0.3"
@@ -4303,7 +4307,8 @@
     "lodash": {
       "version": "4.17.11",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
-      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
+      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+      "dev": true
     },
     "lodash.sortby": {
       "version": "4.7.0",
@@ -5010,7 +5015,8 @@
     "pako": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz",
-      "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA=="
+      "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==",
+      "dev": true
     },
     "parent-module": {
       "version": "1.0.0",


[pulsar-client-node] 26/36: fix error handling about hasNext

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit be89b1682e2729a73d9c15f0869d1b756f49a5f5
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Tue Jun 11 19:03:09 2019 +0900

    fix error handling about hasNext
---
 src/Reader.cc | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/Reader.cc b/src/Reader.cc
index 8c2b859..cfca247 100644
--- a/src/Reader.cc
+++ b/src/Reader.cc
@@ -146,7 +146,10 @@ Napi::Value Reader::ReadNext(const Napi::CallbackInfo &info) {
 Napi::Value Reader::HasNext(const Napi::CallbackInfo &info) {
   int value = 0;
   pulsar_result result = pulsar_reader_has_message_available(this->cReader, &value);
-  if (result != pulsar_result_Ok || value != 1) {
+  if (result != pulsar_result_Ok) {
+    Napi::Error::New(info.Env(), "Failed to check if next message is available").ThrowAsJavaScriptException();
+    return Napi::Boolean::New(info.Env(), false);
+  } else if (value != 1) {
     return Napi::Boolean::New(info.Env(), false);
   } else {
     return Napi::Boolean::New(info.Env(), true);


[pulsar-client-node] 02/36: provide nodejs client

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit f866cd6dd1780763cbee49476b3ae147639eb54f
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Mon Mar 11 18:15:35 2019 +0900

    provide nodejs client
---
 .clang-format         |   25 +
 .eslintrc.json        |    9 +
 .gitignore            |    3 +
 Gruntfile.js          |   31 +
 README.md             |   52 +-
 binding.gyp           |   42 +
 examples/consumer.js  |   47 +
 examples/producer.js  |   51 +
 index.js              |   28 +
 package-lock.json     | 2762 +++++++++++++++++++++++++++++++++++++++++++++++++
 package.json          |   46 +
 perf/perf_consumer.js |  103 ++
 perf/perf_producer.js |  119 +++
 src/Client.cc         |  170 +++
 src/Client.h          |   41 +
 src/Consumer.cc       |  188 ++++
 src/Consumer.h        |   44 +
 src/ConsumerConfig.cc |  106 ++
 src/ConsumerConfig.h  |   44 +
 src/Message.cc        |  197 ++++
 src/Message.h         |   64 ++
 src/MessageId.cc      |   87 ++
 src/MessageId.h       |   46 +
 src/Producer.cc       |  155 +++
 src/Producer.h        |   42 +
 src/ProducerConfig.cc |  159 +++
 src/ProducerConfig.h  |   38 +
 src/addon.cc          |   35 +
 28 files changed, 4733 insertions(+), 1 deletion(-)

diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..cb40b50
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+BasedOnStyle: Google
+IndentWidth: 4
+ColumnLimit: 110
+SortIncludes: false
+BreakBeforeBraces: Custom
+BraceWrapping:
+  AfterEnum: true
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..70e3c60
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,9 @@
+{
+  "extends": "airbnb-base",
+  "rules": {
+    "class-methods-use-this": "warn",
+    "no-await-in-loop": "warn",
+    "no-console": "off",
+    "no-unused-vars": "warn"
+  }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ff60c16
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+build/
+node_modules/
+report/
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..70b901c
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+module.exports = (grunt) => {
+  grunt.loadNpmTasks('grunt-license-report');
+  grunt.initConfig({
+    pkg: grunt.file.readJSON('package.json'),
+    'grunt-license-report': {
+      output: {
+        path: './report/licenses',
+        format: 'html',
+      },
+    },
+  });
+};
diff --git a/README.md b/README.md
index b560743..90d856f 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,51 @@
-# Pulsar Node Client
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+
+# Pulsar Node.js client library
+
+The Pulsar Node.js client can be used to create Pulsar producers and consumers in Node.js.
+
+## Requirements
+
+Pulsar Node.js client library is based on the C++ client library. Follow the instructions for
+[C++ library](https://pulsar.apache.org/docs/en/client-libraries-cpp/) for installing the binaries through
+[RPM](https://pulsar.apache.org/docs/en/client-libraries-cpp/#rpm),
+[Deb](https://pulsar.apache.org/docs/en/client-libraries-cpp/#deb) or
+[Homebrew packages](https://pulsar.apache.org/docs/en/client-libraries-cpp/#macos).
+
+Also, this library works only in Node.js 10.x or later because it uses the
+[node-addon-api](https://github.com/nodejs/node-addon-api) module to wrap the C++ library.
+
+## How to build
+
+### Install dependent npm modules and build Pulsar client library:
+
+```shell
+$ git clone https://github.com/apache/pulsar-client-node.git
+$ cd pulsar-client-node
+$ npm install
+```
+
+### Rebuild Pulsar client library:
+
+```shell
+$ npm run build
+```
diff --git a/binding.gyp b/binding.gyp
new file mode 100644
index 0000000..88a6c70
--- /dev/null
+++ b/binding.gyp
@@ -0,0 +1,42 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+{
+  "targets": [
+    {
+      "target_name": "Pulsar",
+      "cflags!": ["-fno-exceptions"],
+      "cflags_cc!": ["-fno-exceptions"],
+      "include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
+      "dependencies": ["<!@(node -p \"require('node-addon-api').gyp\")"],
+      "defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],
+      "sources": [
+        "src/addon.cc",
+        "src/Message.cc",
+        "src/Client.cc",
+        "src/Producer.cc",
+        "src/ProducerConfig.cc",
+        "src/Consumer.cc",
+        "src/ConsumerConfig.cc",
+        "src/MessageId.cc",
+      ],
+      "libraries": ["-lpulsar"],
+    }
+  ]
+}
diff --git a/examples/consumer.js b/examples/consumer.js
new file mode 100644
index 0000000..7092c6b
--- /dev/null
+++ b/examples/consumer.js
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const Pulsar = require('../index.js');
+
+(async () => {
+  // Create a client
+  const clientConfig = {
+    serviceUrl: 'pulsar://localhost:6650',
+    operationTimeoutSeconds: 30,
+  };
+  const client = new Pulsar.Client(clientConfig);
+
+  // Create a consumer
+  const consumerConfig = {
+    topic: 'persistent://public/default/my-topic',
+    subscription: 'sub1',
+    ackTimeoutMs: 10000,
+  };
+  const consumer = await client.subscribe(consumerConfig);
+
+  // Receive messages
+  for (let i = 0; i < 10; i += 1) {
+    const msg = await consumer.receive();
+    console.log(msg.getData().toString());
+    consumer.acknowledge(msg);
+  }
+
+  await consumer.close();
+  await client.close();
+})();
diff --git a/examples/producer.js b/examples/producer.js
new file mode 100644
index 0000000..b1b6856
--- /dev/null
+++ b/examples/producer.js
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const Pulsar = require('../index.js');
+
+(async () => {
+  // Create a client
+  const clientConfig = {
+    serviceUrl: 'pulsar://localhost:6650',
+    operationTimeoutSeconds: 30,
+  };
+  const client = new Pulsar.Client(clientConfig);
+
+  // Create a producer
+  const producerConfig = {
+    topic: 'persistent://public/default/my-topic',
+    sendTimeoutMs: 30000,
+    batchingEnabled: true,
+  };
+  const producer = await client.createProducer(producerConfig);
+
+  // Send messages
+  const results = [];
+  for (let i = 0; i < 10; i += 1) {
+    const msg = `my-message-${i}`;
+    results.push(producer.send({
+      data: Buffer.from(msg),
+    }));
+    console.log(`Sent message: ${msg}`);
+  }
+  await Promise.all(results);
+
+  await producer.close();
+  await client.close();
+})();
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..03f3a08
--- /dev/null
+++ b/index.js
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const PulsarBinding = require('bindings')('Pulsar');
+
+const Pulsar = {
+  Client: PulsarBinding.Client,
+  Message: PulsarBinding.Message,
+  MessageId: PulsarBinding.MessageId,
+};
+
+module.exports = Pulsar;
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..25d495e
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2762 @@
+{
+  "name": "pulsar-client",
+  "version": "0.0.1",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
+      "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.0.0"
+      }
+    },
+    "@babel/highlight": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
+      "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.0.0",
+        "esutils": "^2.0.2",
+        "js-tokens": "^4.0.0"
+      }
+    },
+    "abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+      "dev": true
+    },
+    "acorn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz",
+      "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==",
+      "dev": true
+    },
+    "acorn-jsx": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
+      "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
+      "dev": true
+    },
+    "ajv": {
+      "version": "6.6.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
+      "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^2.0.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ansi-escapes": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
+      "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "aproba": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+      "dev": true
+    },
+    "are-we-there-yet": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+      "dev": true,
+      "requires": {
+        "delegates": "^1.0.0",
+        "readable-stream": "^2.0.6"
+      }
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "array-find-index": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+      "dev": true
+    },
+    "asap": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+      "dev": true
+    },
+    "asn1": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "dev": true,
+      "requires": {
+        "safer-buffer": "~2.1.0"
+      }
+    },
+    "assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "dev": true
+    },
+    "astral-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+      "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+      "dev": true
+    },
+    "async": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+      "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+      "dev": true
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "dev": true
+    },
+    "aws-sign2": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+      "dev": true
+    },
+    "aws4": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "base64-js": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
+      "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "dev": true,
+      "requires": {
+        "tweetnacl": "^0.14.3"
+      }
+    },
+    "bindings": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz",
+      "integrity": "sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew=="
+    },
+    "block-stream": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
+      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+      "dev": true,
+      "requires": {
+        "inherits": "~2.0.0"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "builtin-modules": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+      "dev": true
+    },
+    "callsites": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+      "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
+      "dev": true
+    },
+    "camelcase": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+      "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+      "dev": true
+    },
+    "camelcase-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+      "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+      "dev": true,
+      "requires": {
+        "camelcase": "^2.0.0",
+        "map-obj": "^1.0.0"
+      }
+    },
+    "caseless": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+      "dev": true
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      }
+    },
+    "chardet": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+      "dev": true
+    },
+    "circular-json": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
+      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
+      "dev": true
+    },
+    "clang-format": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.2.4.tgz",
+      "integrity": "sha512-sw+nrGUp3hvmANd1qF8vZPuezSYQAiXgGBiEtkXTtJnnu6b00fCqkkDIsnRKrNgg4nv6NYZE92ejvOMIXZoejw==",
+      "dev": true,
+      "requires": {
+        "async": "^1.5.2",
+        "glob": "^7.0.0",
+        "resolve": "^1.1.6"
+      }
+    },
+    "cli-cursor": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+      "dev": true,
+      "requires": {
+        "restore-cursor": "^2.0.0"
+      }
+    },
+    "cli-width": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+      "dev": true
+    },
+    "code-point-at": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+      "dev": true
+    },
+    "coffeescript": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
+      "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
+      "dev": true
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "colors": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+      "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+      "dev": true
+    },
+    "combined-stream": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
+      "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
+      "dev": true,
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "commander": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
+      "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "console-control-strings": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
+      "dev": true
+    },
+    "contains-path": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+      "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+      "dev": true
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+      "dev": true
+    },
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+      "dev": true,
+      "requires": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      }
+    },
+    "currently-unhandled": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+      "dev": true,
+      "requires": {
+        "array-find-index": "^1.0.1"
+      }
+    },
+    "dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "dateformat": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
+      "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
+      "dev": true,
+      "requires": {
+        "get-stdin": "^4.0.1",
+        "meow": "^3.3.0"
+      }
+    },
+    "debug": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+      "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+      "dev": true,
+      "requires": {
+        "ms": "^2.1.1"
+      }
+    },
+    "debuglog": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
+      "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=",
+      "dev": true
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true
+    },
+    "deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+      "dev": true
+    },
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "dev": true,
+      "requires": {
+        "object-keys": "^1.0.12"
+      }
+    },
+    "delay": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/delay/-/delay-4.1.0.tgz",
+      "integrity": "sha512-8Hea6/aOu3bPdDBQhSRUEUzF0QwuWmSPuIK+sxNdvcJtSfzb6HXrTd9DFJBCJcV9o83fFECqTgllqdnmUfq9+w=="
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+      "dev": true
+    },
+    "delegates": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+      "dev": true
+    },
+    "dezalgo": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
+      "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
+      "dev": true,
+      "requires": {
+        "asap": "^2.0.0",
+        "wrappy": "1"
+      }
+    },
+    "doctrine": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
+    "ecc-jsbn": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "dev": true,
+      "requires": {
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.1.0"
+      }
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "es-abstract": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
+      "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
+      "dev": true,
+      "requires": {
+        "es-to-primitive": "^1.2.0",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "is-callable": "^1.1.4",
+        "is-regex": "^1.0.4",
+        "object-keys": "^1.0.12"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
+      "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+      "dev": true,
+      "requires": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "eslint": {
+      "version": "5.12.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.12.0.tgz",
+      "integrity": "sha512-LntwyPxtOHrsJdcSwyQKVtHofPHdv+4+mFwEe91r2V13vqpM8yLr7b1sW+Oo/yheOPkWYsYlYJCkzlFAt8KV7g==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "ajv": "^6.5.3",
+        "chalk": "^2.1.0",
+        "cross-spawn": "^6.0.5",
+        "debug": "^4.0.1",
+        "doctrine": "^2.1.0",
+        "eslint-scope": "^4.0.0",
+        "eslint-utils": "^1.3.1",
+        "eslint-visitor-keys": "^1.0.0",
+        "espree": "^5.0.0",
+        "esquery": "^1.0.1",
+        "esutils": "^2.0.2",
+        "file-entry-cache": "^2.0.0",
+        "functional-red-black-tree": "^1.0.1",
+        "glob": "^7.1.2",
+        "globals": "^11.7.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "inquirer": "^6.1.0",
+        "js-yaml": "^3.12.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.3.0",
+        "lodash": "^4.17.5",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.1",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.8.2",
+        "path-is-inside": "^1.0.2",
+        "pluralize": "^7.0.0",
+        "progress": "^2.0.0",
+        "regexpp": "^2.0.1",
+        "semver": "^5.5.1",
+        "strip-ansi": "^4.0.0",
+        "strip-json-comments": "^2.0.1",
+        "table": "^5.0.2",
+        "text-table": "^0.2.0"
+      }
+    },
+    "eslint-config-airbnb-base": {
+      "version": "13.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz",
+      "integrity": "sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw==",
+      "dev": true,
+      "requires": {
+        "eslint-restricted-globals": "^0.1.1",
+        "object.assign": "^4.1.0",
+        "object.entries": "^1.0.4"
+      }
+    },
+    "eslint-import-resolver-node": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
+      "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
+      "dev": true,
+      "requires": {
+        "debug": "^2.6.9",
+        "resolve": "^1.5.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "eslint-module-utils": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz",
+      "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=",
+      "dev": true,
+      "requires": {
+        "debug": "^2.6.8",
+        "pkg-dir": "^1.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "eslint-plugin-import": {
+      "version": "2.14.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz",
+      "integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==",
+      "dev": true,
+      "requires": {
+        "contains-path": "^0.1.0",
+        "debug": "^2.6.8",
+        "doctrine": "1.5.0",
+        "eslint-import-resolver-node": "^0.3.1",
+        "eslint-module-utils": "^2.2.0",
+        "has": "^1.0.1",
+        "lodash": "^4.17.4",
+        "minimatch": "^3.0.3",
+        "read-pkg-up": "^2.0.0",
+        "resolve": "^1.6.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "doctrine": {
+          "version": "1.5.0",
+          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+          "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+          "dev": true,
+          "requires": {
+            "esutils": "^2.0.2",
+            "isarray": "^1.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "eslint-restricted-globals": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz",
+      "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=",
+      "dev": true
+    },
+    "eslint-scope": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
+      "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.1.0",
+        "estraverse": "^4.1.1"
+      }
+    },
+    "eslint-utils": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
+      "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
+      "dev": true
+    },
+    "eslint-visitor-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+      "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+      "dev": true
+    },
+    "espree": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.0.tgz",
+      "integrity": "sha512-1MpUfwsdS9MMoN7ZXqAr9e9UKdVHDcvrJpyx7mm1WuQlx/ygErEQBzgi5Nh5qBHIoYweprhtMkTCb9GhcAIcsA==",
+      "dev": true,
+      "requires": {
+        "acorn": "^6.0.2",
+        "acorn-jsx": "^5.0.0",
+        "eslint-visitor-keys": "^1.0.0"
+      }
+    },
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true
+    },
+    "esquery": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+      "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^4.0.0"
+      }
+    },
+    "esrecurse": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^4.1.0"
+      }
+    },
+    "estraverse": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+      "dev": true
+    },
+    "esutils": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+      "dev": true
+    },
+    "eventemitter2": {
+      "version": "0.4.14",
+      "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+      "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
+      "dev": true
+    },
+    "exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+      "dev": true
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true
+    },
+    "external-editor": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
+      "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+      "dev": true,
+      "requires": {
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
+      }
+    },
+    "extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "dev": true
+    },
+    "fast-deep-equal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
+    "figures": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+      "dev": true,
+      "requires": {
+        "escape-string-regexp": "^1.0.5"
+      }
+    },
+    "file-entry-cache": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+      "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^1.2.1",
+        "object-assign": "^4.0.1"
+      }
+    },
+    "find-up": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+      "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+      "dev": true,
+      "requires": {
+        "path-exists": "^2.0.0",
+        "pinkie-promise": "^2.0.0"
+      }
+    },
+    "findup-sync": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
+      "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
+      "dev": true,
+      "requires": {
+        "glob": "~5.0.0"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "5.0.15",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+          "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+          "dev": true,
+          "requires": {
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "2 || 3",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        }
+      }
+    },
+    "flat-cache": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
+      "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
+      "dev": true,
+      "requires": {
+        "circular-json": "^0.3.1",
+        "graceful-fs": "^4.1.2",
+        "rimraf": "~2.6.2",
+        "write": "^0.2.1"
+      }
+    },
+    "forever-agent": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "dev": true
+    },
+    "form-data": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+      "dev": true,
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "fstream": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
+      "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "inherits": "~2.0.0",
+        "mkdirp": ">=0.5 0",
+        "rimraf": "2"
+      }
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "dev": true
+    },
+    "gauge": {
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.0.3",
+        "console-control-strings": "^1.0.0",
+        "has-unicode": "^2.0.0",
+        "object-assign": "^4.1.0",
+        "signal-exit": "^3.0.0",
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wide-align": "^1.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "dev": true,
+          "requires": {
+            "number-is-nan": "^1.0.0"
+          }
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "dev": true,
+          "requires": {
+            "code-point-at": "^1.0.0",
+            "is-fullwidth-code-point": "^1.0.0",
+            "strip-ansi": "^3.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^2.0.0"
+          }
+        }
+      }
+    },
+    "get-stdin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+      "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+      "dev": true
+    },
+    "getobject": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
+      "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
+      "dev": true
+    },
+    "getpass": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "github-url-from-git": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/github-url-from-git/-/github-url-from-git-1.5.0.tgz",
+      "integrity": "sha1-+YX+3MCpqledyI16/waNVcxiUaA=",
+      "dev": true
+    },
+    "github-url-from-username-repo": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/github-url-from-username-repo/-/github-url-from-username-repo-1.0.2.tgz",
+      "integrity": "sha1-fdeTMNKr5pwQws73lxTJchV5Hfo=",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "globals": {
+      "version": "11.10.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz",
+      "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==",
+      "dev": true
+    },
+    "graceful-fs": {
+      "version": "4.1.15",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+      "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+      "dev": true
+    },
+    "grunt": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.3.tgz",
+      "integrity": "sha512-/JzmZNPfKorlCrrmxWqQO4JVodO+DVd5XX4DkocL/1WlLlKVLE9+SdEIempOAxDhWPysLle6afvn/hg7Ck2k9g==",
+      "dev": true,
+      "requires": {
+        "coffeescript": "~1.10.0",
+        "dateformat": "~1.0.12",
+        "eventemitter2": "~0.4.13",
+        "exit": "~0.1.1",
+        "findup-sync": "~0.3.0",
+        "glob": "~7.0.0",
+        "grunt-cli": "~1.2.0",
+        "grunt-known-options": "~1.1.0",
+        "grunt-legacy-log": "~2.0.0",
+        "grunt-legacy-util": "~1.1.1",
+        "iconv-lite": "~0.4.13",
+        "js-yaml": "~3.5.2",
+        "minimatch": "~3.0.2",
+        "mkdirp": "~0.5.1",
+        "nopt": "~3.0.6",
+        "path-is-absolute": "~1.0.0",
+        "rimraf": "~2.6.2"
+      },
+      "dependencies": {
+        "esprima": {
+          "version": "2.7.3",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+          "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+          "dev": true
+        },
+        "glob": {
+          "version": "7.0.6",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+          "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.2",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "grunt-cli": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
+          "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
+          "dev": true,
+          "requires": {
+            "findup-sync": "~0.3.0",
+            "grunt-known-options": "~1.1.0",
+            "nopt": "~3.0.6",
+            "resolve": "~1.1.0"
+          }
+        },
+        "js-yaml": {
+          "version": "3.5.5",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz",
+          "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=",
+          "dev": true,
+          "requires": {
+            "argparse": "^1.0.2",
+            "esprima": "^2.6.0"
+          }
+        },
+        "resolve": {
+          "version": "1.1.7",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+          "dev": true
+        }
+      }
+    },
+    "grunt-known-options": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz",
+      "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==",
+      "dev": true
+    },
+    "grunt-legacy-log": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz",
+      "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==",
+      "dev": true,
+      "requires": {
+        "colors": "~1.1.2",
+        "grunt-legacy-log-utils": "~2.0.0",
+        "hooker": "~0.2.3",
+        "lodash": "~4.17.5"
+      }
+    },
+    "grunt-legacy-log-utils": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz",
+      "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==",
+      "dev": true,
+      "requires": {
+        "chalk": "~2.4.1",
+        "lodash": "~4.17.10"
+      }
+    },
+    "grunt-legacy-util": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz",
+      "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==",
+      "dev": true,
+      "requires": {
+        "async": "~1.5.2",
+        "exit": "~0.1.1",
+        "getobject": "~0.1.0",
+        "hooker": "~0.2.3",
+        "lodash": "~4.17.10",
+        "underscore.string": "~3.3.4",
+        "which": "~1.3.0"
+      }
+    },
+    "grunt-license-report": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/grunt-license-report/-/grunt-license-report-0.0.8.tgz",
+      "integrity": "sha1-/gn4e+ddPgBpj4MGAF2PkzsPNv4=",
+      "dev": true,
+      "requires": {
+        "license-checker": "2.0.1"
+      }
+    },
+    "har-schema": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+      "dev": true
+    },
+    "har-validator": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.5.5",
+        "har-schema": "^2.0.0"
+      }
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-ansi": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
+      "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^0.2.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+          "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
+          "dev": true
+        }
+      }
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "has-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+      "dev": true
+    },
+    "has-unicode": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+      "dev": true
+    },
+    "hdr-histogram-js": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-1.1.4.tgz",
+      "integrity": "sha1-dBNAJdGuHP711/ESfrdWDeKgp3M=",
+      "requires": {
+        "base64-js": "^1.2.0",
+        "pako": "^1.0.3"
+      }
+    },
+    "hooker": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
+      "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
+      "dev": true
+    },
+    "hosted-git-info": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
+      "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+      "dev": true
+    },
+    "http-signature": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "jsprim": "^1.2.2",
+        "sshpk": "^1.7.0"
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dev": true,
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "ignore": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+      "dev": true
+    },
+    "import-fresh": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz",
+      "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+      "dev": true
+    },
+    "indent-string": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+      "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+      "dev": true,
+      "requires": {
+        "repeating": "^2.0.0"
+      }
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+      "dev": true
+    },
+    "inquirer": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz",
+      "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==",
+      "dev": true,
+      "requires": {
+        "ansi-escapes": "^3.0.0",
+        "chalk": "^2.0.0",
+        "cli-cursor": "^2.1.0",
+        "cli-width": "^2.0.0",
+        "external-editor": "^3.0.0",
+        "figures": "^2.0.0",
+        "lodash": "^4.17.10",
+        "mute-stream": "0.0.7",
+        "run-async": "^2.2.0",
+        "rxjs": "^6.1.0",
+        "string-width": "^2.1.0",
+        "strip-ansi": "^5.0.0",
+        "through": "^2.3.6"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+          "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+          "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.0.0"
+          }
+        }
+      }
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
+    "is-builtin-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+      "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+      "dev": true,
+      "requires": {
+        "builtin-modules": "^1.0.0"
+      }
+    },
+    "is-callable": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+      "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
+      "dev": true
+    },
+    "is-date-object": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
+      "dev": true
+    },
+    "is-finite": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+      "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+      "dev": true,
+      "requires": {
+        "number-is-nan": "^1.0.0"
+      }
+    },
+    "is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+      "dev": true
+    },
+    "is-promise": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+      "dev": true
+    },
+    "is-regex": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.1"
+      }
+    },
+    "is-symbol": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+      "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+      "dev": true,
+      "requires": {
+        "has-symbols": "^1.0.0"
+      }
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "dev": true
+    },
+    "is-utf8": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+      "dev": true
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+      "dev": true
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+      "dev": true
+    },
+    "jju": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
+      "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=",
+      "dev": true
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "3.12.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
+      "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
+      "dev": true,
+      "requires": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      }
+    },
+    "jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+      "dev": true
+    },
+    "json-parse-helpfulerror": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz",
+      "integrity": "sha1-E/FM4C7tTpgSl7ZOueO5MuLdE9w=",
+      "dev": true,
+      "requires": {
+        "jju": "^1.1.0"
+      }
+    },
+    "json-schema": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "dev": true
+    },
+    "json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+      "dev": true
+    },
+    "jsprim": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.3.0",
+        "json-schema": "0.2.3",
+        "verror": "1.10.0"
+      }
+    },
+    "levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      }
+    },
+    "license-checker": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-2.0.1.tgz",
+      "integrity": "sha1-mrCsHX5FfvsLuXp3PoPBbOfyIvI=",
+      "dev": true,
+      "requires": {
+        "chalk": "~0.5.1",
+        "mkdirp": "^0.3.5",
+        "nopt": "^2.2.0",
+        "read-installed": "~3.1.3",
+        "treeify": "^1.0.1"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+          "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
+          "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "0.5.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
+          "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^1.1.0",
+            "escape-string-regexp": "^1.0.0",
+            "has-ansi": "^0.1.0",
+            "strip-ansi": "^0.3.0",
+            "supports-color": "^0.2.0"
+          }
+        },
+        "mkdirp": {
+          "version": "0.3.5",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
+          "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=",
+          "dev": true
+        },
+        "nopt": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.2.1.tgz",
+          "integrity": "sha1-KqCbfRdoSHs7ianFqlIzW/8Lrqc=",
+          "dev": true,
+          "requires": {
+            "abbrev": "1"
+          }
+        },
+        "strip-ansi": {
+          "version": "0.3.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
+          "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^0.2.1"
+          }
+        },
+        "supports-color": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
+          "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=",
+          "dev": true
+        }
+      }
+    },
+    "load-json-file": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+      "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^2.2.0",
+        "pify": "^2.0.0",
+        "strip-bom": "^3.0.0"
+      }
+    },
+    "locate-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+      "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+      "dev": true,
+      "requires": {
+        "p-locate": "^2.0.0",
+        "path-exists": "^3.0.0"
+      },
+      "dependencies": {
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        }
+      }
+    },
+    "lodash": {
+      "version": "4.17.11",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+      "dev": true
+    },
+    "loud-rejection": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+      "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+      "dev": true,
+      "requires": {
+        "currently-unhandled": "^0.4.1",
+        "signal-exit": "^3.0.0"
+      }
+    },
+    "map-obj": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+      "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+      "dev": true
+    },
+    "meow": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+      "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+      "dev": true,
+      "requires": {
+        "camelcase-keys": "^2.0.0",
+        "decamelize": "^1.1.2",
+        "loud-rejection": "^1.0.0",
+        "map-obj": "^1.0.1",
+        "minimist": "^1.1.3",
+        "normalize-package-data": "^2.3.4",
+        "object-assign": "^4.0.1",
+        "read-pkg-up": "^1.0.1",
+        "redent": "^1.0.0",
+        "trim-newlines": "^1.0.0"
+      },
+      "dependencies": {
+        "load-json-file": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+          "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "parse-json": "^2.2.0",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0",
+            "strip-bom": "^2.0.0"
+          }
+        },
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        },
+        "path-type": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+          "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0"
+          }
+        },
+        "read-pkg": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+          "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+          "dev": true,
+          "requires": {
+            "load-json-file": "^1.0.0",
+            "normalize-package-data": "^2.3.2",
+            "path-type": "^1.0.0"
+          }
+        },
+        "read-pkg-up": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+          "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+          "dev": true,
+          "requires": {
+            "find-up": "^1.0.0",
+            "read-pkg": "^1.0.0"
+          }
+        },
+        "strip-bom": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+          "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+          "dev": true,
+          "requires": {
+            "is-utf8": "^0.2.0"
+          }
+        }
+      }
+    },
+    "mime-db": {
+      "version": "1.37.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
+      "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==",
+      "dev": true
+    },
+    "mime-types": {
+      "version": "2.1.21",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
+      "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
+      "dev": true,
+      "requires": {
+        "mime-db": "~1.37.0"
+      }
+    },
+    "mimic-fn": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+      "dev": true
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "dev": true,
+      "requires": {
+        "minimist": "0.0.8"
+      }
+    },
+    "ms": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+      "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+      "dev": true
+    },
+    "mute-stream": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+      "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+      "dev": true
+    },
+    "natives": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz",
+      "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==",
+      "dev": true,
+      "optional": true
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+      "dev": true
+    },
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
+    },
+    "node-addon-api": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.6.2.tgz",
+      "integrity": "sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA==",
+      "dev": true
+    },
+    "node-gyp": {
+      "version": "3.8.0",
+      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
+      "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
+      "dev": true,
+      "requires": {
+        "fstream": "^1.0.0",
+        "glob": "^7.0.3",
+        "graceful-fs": "^4.1.2",
+        "mkdirp": "^0.5.0",
+        "nopt": "2 || 3",
+        "npmlog": "0 || 1 || 2 || 3 || 4",
+        "osenv": "0",
+        "request": "^2.87.0",
+        "rimraf": "2",
+        "semver": "~5.3.0",
+        "tar": "^2.0.0",
+        "which": "1"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+          "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+          "dev": true
+        }
+      }
+    },
+    "nopt": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+      "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+      "dev": true,
+      "requires": {
+        "abbrev": "1"
+      }
+    },
+    "normalize-package-data": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "is-builtin-module": "^1.0.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "npmlog": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+      "dev": true,
+      "requires": {
+        "are-we-there-yet": "~1.1.2",
+        "console-control-strings": "~1.1.0",
+        "gauge": "~2.7.3",
+        "set-blocking": "~2.0.0"
+      }
+    },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+      "dev": true
+    },
+    "oauth-sign": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+      "dev": true
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+      "dev": true
+    },
+    "object-keys": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
+      "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
+      "dev": true
+    },
+    "object.assign": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.2",
+        "function-bind": "^1.1.1",
+        "has-symbols": "^1.0.0",
+        "object-keys": "^1.0.11"
+      }
+    },
+    "object.entries": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz",
+      "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.12.0",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3"
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "onetime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+      "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "^1.0.0"
+      }
+    },
+    "optionator": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+      "dev": true,
+      "requires": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.4",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "wordwrap": "~1.0.0"
+      }
+    },
+    "os-homedir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+      "dev": true
+    },
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+      "dev": true
+    },
+    "osenv": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+      "dev": true,
+      "requires": {
+        "os-homedir": "^1.0.0",
+        "os-tmpdir": "^1.0.0"
+      }
+    },
+    "p-limit": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+      "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+      "dev": true,
+      "requires": {
+        "p-try": "^1.0.0"
+      }
+    },
+    "p-locate": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+      "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+      "dev": true,
+      "requires": {
+        "p-limit": "^1.1.0"
+      }
+    },
+    "p-try": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+      "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+      "dev": true
+    },
+    "pako": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz",
+      "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA=="
+    },
+    "parent-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz",
+      "integrity": "sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
+    "parse-json": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+      "dev": true,
+      "requires": {
+        "error-ex": "^1.2.0"
+      }
+    },
+    "path-exists": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+      "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+      "dev": true,
+      "requires": {
+        "pinkie-promise": "^2.0.0"
+      }
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "path-is-inside": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+      "dev": true
+    },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+      "dev": true
+    },
+    "path-type": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+      "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+      "dev": true,
+      "requires": {
+        "pify": "^2.0.0"
+      }
+    },
+    "performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+      "dev": true
+    },
+    "pify": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+      "dev": true
+    },
+    "pinkie": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+      "dev": true
+    },
+    "pinkie-promise": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+      "dev": true,
+      "requires": {
+        "pinkie": "^2.0.0"
+      }
+    },
+    "pkg-dir": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
+      "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
+      "dev": true,
+      "requires": {
+        "find-up": "^1.0.0"
+      }
+    },
+    "pluralize": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
+      "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
+      "dev": true
+    },
+    "prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+      "dev": true
+    },
+    "process-nextick-args": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+      "dev": true
+    },
+    "progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true
+    },
+    "psl": {
+      "version": "1.1.31",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
+      "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==",
+      "dev": true
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true
+    },
+    "qs": {
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+      "dev": true
+    },
+    "read-installed": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-3.1.5.tgz",
+      "integrity": "sha1-SuNgga/T4iBNwuJ5gHqqUsMMjAw=",
+      "dev": true,
+      "requires": {
+        "debuglog": "^1.0.1",
+        "graceful-fs": "2 || 3",
+        "read-package-json": "1",
+        "readdir-scoped-modules": "^1.0.0",
+        "semver": "2 || 3 || 4",
+        "slide": "~1.1.3",
+        "util-extend": "^1.0.1"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "3.0.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz",
+          "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "natives": "^1.1.0"
+          }
+        },
+        "semver": {
+          "version": "4.3.6",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
+          "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=",
+          "dev": true
+        }
+      }
+    },
+    "read-package-json": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-1.3.3.tgz",
+      "integrity": "sha1-73nf2kbhZTdu6KV++/7dTRsCm6Q=",
+      "dev": true,
+      "requires": {
+        "glob": "^5.0.3",
+        "graceful-fs": "2 || 3",
+        "json-parse-helpfulerror": "^1.0.2",
+        "normalize-package-data": "^1.0.0"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "5.0.15",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+          "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+          "dev": true,
+          "requires": {
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "2 || 3",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "graceful-fs": {
+          "version": "3.0.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz",
+          "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "natives": "^1.1.0"
+          }
+        },
+        "normalize-package-data": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-1.0.3.tgz",
+          "integrity": "sha1-i+lVuJB6+XXxpFhOqLubQUkjEvU=",
+          "dev": true,
+          "requires": {
+            "github-url-from-git": "^1.3.0",
+            "github-url-from-username-repo": "^1.0.0",
+            "semver": "2 || 3 || 4"
+          }
+        },
+        "semver": {
+          "version": "4.3.6",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
+          "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=",
+          "dev": true
+        }
+      }
+    },
+    "read-pkg": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+      "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+      "dev": true,
+      "requires": {
+        "load-json-file": "^2.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^2.0.0"
+      }
+    },
+    "read-pkg-up": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+      "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+      "dev": true,
+      "requires": {
+        "find-up": "^2.0.0",
+        "read-pkg": "^2.0.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+          "dev": true,
+          "requires": {
+            "locate-path": "^2.0.0"
+          }
+        }
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+      "dev": true,
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "readdir-scoped-modules": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz",
+      "integrity": "sha1-n6+jfShr5dksuuve4DDcm19AZ0c=",
+      "dev": true,
+      "requires": {
+        "debuglog": "^1.0.1",
+        "dezalgo": "^1.0.0",
+        "graceful-fs": "^4.1.2",
+        "once": "^1.3.0"
+      }
+    },
+    "redent": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+      "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+      "dev": true,
+      "requires": {
+        "indent-string": "^2.1.0",
+        "strip-indent": "^1.0.1"
+      }
+    },
+    "regexpp": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+      "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+      "dev": true
+    },
+    "repeating": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+      "dev": true,
+      "requires": {
+        "is-finite": "^1.0.0"
+      }
+    },
+    "request": {
+      "version": "2.88.0",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+      "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+      "dev": true,
+      "requires": {
+        "aws-sign2": "~0.7.0",
+        "aws4": "^1.8.0",
+        "caseless": "~0.12.0",
+        "combined-stream": "~1.0.6",
+        "extend": "~3.0.2",
+        "forever-agent": "~0.6.1",
+        "form-data": "~2.3.2",
+        "har-validator": "~5.1.0",
+        "http-signature": "~1.2.0",
+        "is-typedarray": "~1.0.0",
+        "isstream": "~0.1.2",
+        "json-stringify-safe": "~5.0.1",
+        "mime-types": "~2.1.19",
+        "oauth-sign": "~0.9.0",
+        "performance-now": "^2.1.0",
+        "qs": "~6.5.2",
+        "safe-buffer": "^5.1.2",
+        "tough-cookie": "~2.4.3",
+        "tunnel-agent": "^0.6.0",
+        "uuid": "^3.3.2"
+      }
+    },
+    "resolve": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz",
+      "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==",
+      "dev": true,
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true
+    },
+    "restore-cursor": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+      "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+      "dev": true,
+      "requires": {
+        "onetime": "^2.0.0",
+        "signal-exit": "^3.0.2"
+      }
+    },
+    "rimraf": {
+      "version": "2.6.3",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "run-async": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+      "dev": true,
+      "requires": {
+        "is-promise": "^2.1.0"
+      }
+    },
+    "rxjs": {
+      "version": "6.3.3",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+      "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "dev": true
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true
+    },
+    "semver": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+      "dev": true
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+      "dev": true
+    },
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^1.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true
+    },
+    "signal-exit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+      "dev": true
+    },
+    "slice-ansi": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.0.0.tgz",
+      "integrity": "sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.0",
+        "astral-regex": "^1.0.0",
+        "is-fullwidth-code-point": "^2.0.0"
+      }
+    },
+    "slide": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
+      "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=",
+      "dev": true
+    },
+    "spdx-correct": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+      "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+      "dev": true,
+      "requires": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+      "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+      "dev": true
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+      "dev": true,
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz",
+      "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==",
+      "dev": true
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "sshpk": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz",
+      "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==",
+      "dev": true,
+      "requires": {
+        "asn1": "~0.2.3",
+        "assert-plus": "^1.0.0",
+        "bcrypt-pbkdf": "^1.0.0",
+        "dashdash": "^1.12.0",
+        "ecc-jsbn": "~0.1.1",
+        "getpass": "^0.1.1",
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.0.2",
+        "tweetnacl": "~0.14.0"
+      }
+    },
+    "string-width": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+      "dev": true,
+      "requires": {
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^4.0.0"
+      }
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+      "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^3.0.0"
+      }
+    },
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "dev": true
+    },
+    "strip-indent": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+      "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+      "dev": true,
+      "requires": {
+        "get-stdin": "^4.0.1"
+      }
+    },
+    "strip-json-comments": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "table": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/table/-/table-5.1.1.tgz",
+      "integrity": "sha512-NUjapYb/qd4PeFW03HnAuOJ7OMcBkJlqeClWxeNlQ0lXGSb52oZXGzkO0/I0ARegQ2eUT1g2VDJH0eUxDRcHmw==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.6.1",
+        "lodash": "^4.17.11",
+        "slice-ansi": "2.0.0",
+        "string-width": "^2.1.1"
+      }
+    },
+    "tar": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
+      "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
+      "dev": true,
+      "requires": {
+        "block-stream": "*",
+        "fstream": "^1.0.2",
+        "inherits": "2"
+      }
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+      "dev": true
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+      "dev": true
+    },
+    "tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "dev": true,
+      "requires": {
+        "os-tmpdir": "~1.0.2"
+      }
+    },
+    "tough-cookie": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+      "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+      "dev": true,
+      "requires": {
+        "psl": "^1.1.24",
+        "punycode": "^1.4.1"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        }
+      }
+    },
+    "treeify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
+      "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==",
+      "dev": true
+    },
+    "trim-newlines": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+      "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+      "dev": true
+    },
+    "tslib": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+      "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+      "dev": true
+    },
+    "tunnel-agent": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "tweetnacl": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+      "dev": true
+    },
+    "type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2"
+      }
+    },
+    "underscore.string": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
+      "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "^1.0.3",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "uri-js": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+      "dev": true
+    },
+    "util-extend": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
+      "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=",
+      "dev": true
+    },
+    "uuid": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+      "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+      "dev": true
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "verror": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      }
+    },
+    "which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "wide-align": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+      "dev": true,
+      "requires": {
+        "string-width": "^1.0.2 || 2"
+      }
+    },
+    "wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+      "dev": true
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "write": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+      "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+      "dev": true,
+      "requires": {
+        "mkdirp": "^0.5.1"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..33b1321
--- /dev/null
+++ b/package.json
@@ -0,0 +1,46 @@
+{
+  "name": "pulsar-client",
+  "version": "0.0.1",
+  "description": "Pulsar Node.js client",
+  "main": "index.js",
+  "directories": {
+    "src": "src",
+    "example": "examples"
+  },
+  "scripts": {
+    "clean": "node-gyp clean",
+    "configure": "node-gyp configure",
+    "lint": "eslint --ext .js .",
+    "format": "clang-format -i --verbose src/* && eslint --fix --ext .js .",
+    "build": "npm run format && node-gyp rebuild",
+    "build:debug": "npm run format && node-gyp rebuild --debug",
+    "license:report": "mkdir -p report && grunt grunt-license-report",
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/apache/pulsar-client-node.git"
+  },
+  "author": "Apache Software Foundation",
+  "license": "Apache-2.0",
+  "gypfile": true,
+  "engines": {
+    "node": ">=10.0.0"
+  },
+  "devDependencies": {
+    "clang-format": "^1.2.4",
+    "eslint": "^5.12.0",
+    "eslint-config-airbnb-base": "^13.1.0",
+    "eslint-plugin-import": "^2.14.0",
+    "grunt": "^1.0.3",
+    "grunt-license-report": "^0.0.8",
+    "node-addon-api": "^1.6.2",
+    "node-gyp": "^3.8.0"
+  },
+  "dependencies": {
+    "bindings": "^1.3.1",
+    "commander": "^2.19.0",
+    "delay": "^4.1.0",
+    "hdr-histogram-js": "^1.1.4"
+  }
+}
diff --git a/perf/perf_consumer.js b/perf/perf_consumer.js
new file mode 100644
index 0000000..717eb45
--- /dev/null
+++ b/perf/perf_consumer.js
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const commander = require('commander');
+const delay = require('delay');
+const {
+  performance,
+} = require('perf_hooks');
+const Pulsar = require('../index.js');
+
+// Parse args
+(() => {
+  commander
+    .option('-s, --subscription [subscription]', 'Subscription')
+    .option('-u, --url [url]', 'Pulsar Service URL')
+    .option('-t, --topic [topic]', 'Topic Name')
+    .option('-m, --messages [messages]', 'Number of Messages', 1000)
+    .option('-i, --iteration [iteration]', 'Iteration of Measure', 3, parseInt)
+    .on('--help', () => {
+      console.log('');
+      console.log('Examples:');
+      console.log('  $ node perf/perf_consumer.js --subscription sub1 --url pulsar://localhost:6650 --topic persistent://public/default/my-topic');
+    })
+    .parse(process.argv);
+
+  if (typeof commander.subscription === 'undefined') {
+    console.error('no subscription given!');
+    process.exit(1);
+  }
+  if (typeof commander.url === 'undefined') {
+    console.error('no URL given!');
+    process.exit(1);
+  }
+  if (typeof commander.topic === 'undefined') {
+    console.error('no topic name given!');
+    process.exit(1);
+  }
+
+  console.log('----------------------');
+  commander.options.forEach((option) => {
+    const optionName = (option.long).replace('--', '');
+    console.log(`${optionName}: ${commander[optionName]}`);
+  });
+  console.log('----------------------');
+})();
+
+(async () => {
+  // Create a client
+  const clientConfig = {
+    serviceUrl: commander.url,
+  };
+  const client = new Pulsar.Client(clientConfig);
+
+  // Create a consumer
+  const consumerConfig = {
+    topic: commander.topic,
+    subscription: commander.subscription,
+  };
+  const consumer = await client.subscribe(consumerConfig);
+
+  const numOfMessages = parseInt(commander.messages, 10);
+  for (let i = 0; i < commander.iteration; i += 1) {
+    // measure
+    await delay(1000);
+    const startTimeMilliSeconds = performance.now();
+    const results = [];
+    for (let mi = 0; mi < numOfMessages; mi += 1) {
+      results.push(new Promise((resolve) => {
+        consumer.receive().then((msg) => {
+          consumer.acknowledge(msg);
+          resolve(msg.getData().length);
+        });
+      }));
+    }
+    const messageSizes = await Promise.all(results);
+    const endTimeMilliSeconds = performance.now();
+
+    // result
+    const durationSeconds = (endTimeMilliSeconds - startTimeMilliSeconds) / 1000.0;
+    const rate = numOfMessages / durationSeconds;
+    const totalMessageSize = messageSizes.reduce((a, b) => a + b);
+    const throuhputMbit = (totalMessageSize / 1024 / 1024 * 8) / durationSeconds;
+    console.log('Throughput received: %f  msg/s --- %f Mbit/s', rate.toFixed(3), throuhputMbit.toFixed(3));
+  }
+  await consumer.close();
+  await client.close();
+})();
diff --git a/perf/perf_producer.js b/perf/perf_producer.js
new file mode 100644
index 0000000..19f251c
--- /dev/null
+++ b/perf/perf_producer.js
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const commander = require('commander');
+const delay = require('delay');
+const hdr = require('hdr-histogram-js');
+const {
+  performance,
+} = require('perf_hooks');
+
+const Pulsar = require('../index.js');
+
+// Parse args
+(() => {
+  commander
+    .option('-s, --size [size]', 'Message Size', 1024, parseInt)
+    .option('-u, --url [url]', 'Pulsar Service URL')
+    .option('-t, --topic [topic]', 'Topic Name')
+    .option('-i, --iteration [iteration]', 'Iteration of Measure', 3, parseInt)
+    .option('-m, --messages [messages]', 'Number of Messages', 1000, parseInt)
+    .on('--help', () => {
+      console.log('');
+      console.log('Examples:');
+      console.log('  $ node perf/perf_producer.js --url pulsar://localhost:6650 --topic persistent://public/default/my-topic');
+    })
+    .parse(process.argv);
+
+  if (typeof commander.url === 'undefined') {
+    console.error('no URL given!');
+    process.exit(1);
+  }
+  if (typeof commander.topic === 'undefined') {
+    console.error('no topic name given!');
+    process.exit(1);
+  }
+
+  console.log('----------------------');
+  commander.options.forEach((option) => {
+    const optionName = (option.long).replace('--', '');
+    console.log(`${optionName}: ${commander[optionName]}`);
+  });
+  console.log('----------------------');
+})();
+
+// Produce
+(async () => {
+  // Create a client
+  const clientConfig = {
+    serviceUrl: commander.url,
+  };
+  const client = new Pulsar.Client(clientConfig);
+
+  // Create a producer
+  const producerConfig = {
+    topic: commander.topic,
+  };
+  const producer = await client.createProducer(producerConfig);
+
+  const sizeOfMessage = commander.size;
+  const message = Buffer.alloc(sizeOfMessage, 1);
+  const numOfMessages = commander.messages;
+  for (let i = 0; i < commander.iteration; i += 1) {
+    const histogram = hdr.build({
+      bitBucketSize: 0,
+      highestTrackableValue: 120000 * 1000,
+      numberOfSignificantValueDigits: 5,
+    });
+
+    // measure
+    await delay(1000);
+    const startMeasureTimeMilliSeconds = performance.now();
+    const results = [];
+    for (let mi = 0; mi < numOfMessages; mi += 1) {
+      const startSendTimeMilliSeconds = performance.now();
+      results.push(producer.send({
+        data: message,
+      }).then(() => {
+        // add latency
+        histogram.recordValue((performance.now() - startSendTimeMilliSeconds));
+      }));
+    }
+    await Promise.all(results);
+    const endMeasureTimeMilliSeconds = performance.now();
+
+    // result
+    const elapsedSeconds = (endMeasureTimeMilliSeconds - startMeasureTimeMilliSeconds) / 1000;
+    const rate = numOfMessages / elapsedSeconds;
+    const throuhputMbit = (sizeOfMessage * numOfMessages / 1024 / 1024 * 8) / elapsedSeconds;
+    console.log('Throughput produced: %f  msg/s --- %f Mbit/s --- Latency: mean: %f ms - med: %f - 95pct: %f - 99pct: %f - 99.9pct: %f - 99.99pct: %f - Max: %f',
+      rate.toFixed(3),
+      throuhputMbit.toFixed(3),
+      histogram.getMean(),
+      histogram.getValueAtPercentile(50),
+      histogram.getValueAtPercentile(95),
+      histogram.getValueAtPercentile(99),
+      histogram.getValueAtPercentile(99.9),
+      histogram.getValueAtPercentile(99.99),
+      histogram.maxValue);
+  }
+
+  await producer.close();
+  await client.close();
+})();
diff --git a/src/Client.cc b/src/Client.cc
new file mode 100644
index 0000000..97fc573
--- /dev/null
+++ b/src/Client.cc
@@ -0,0 +1,170 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "Client.h"
+#include "Consumer.h"
+#include "Producer.h"
+#include <pulsar/c/client.h>
+#include <pulsar/c/client_configuration.h>
+#include <pulsar/c/result.h>
+
+static const std::string CFG_SERVICE_URL = "serviceUrl";
+static const std::string CFG_OP_TIMEOUT = "operationTimeoutSeconds";
+static const std::string CFG_IO_THREADS = "ioThreads";
+static const std::string CFG_LISTENER_THREADS = "messageListenerThreads";
+static const std::string CFG_CONCURRENT_LOOKUP = "concurrentLookupRequest";
+static const std::string CFG_USE_TLS = "useTls";
+static const std::string CFG_TLS_TRUST_CERT = "tlsTrustCertsFilePath";
+static const std::string CFG_TLS_VALIDATE_HOSTNAME = "tlsValidateHostname";
+static const std::string CFG_TLS_ALLOW_INSECURE = "tlsAllowInsecureConnection";
+static const std::string CFG_STATS_INTERVAL = "statsIntervalInSeconds";
+
+Napi::FunctionReference Client::constructor;
+
+Napi::Object Client::Init(Napi::Env env, Napi::Object exports) {
+    Napi::HandleScope scope(env);
+
+    Napi::Function func = DefineClass(
+        env, "Client",
+        {InstanceMethod("createProducer", &Client::CreateProducer),
+         InstanceMethod("subscribe", &Client::Subscribe), InstanceMethod("close", &Client::Close)});
+
+    constructor = Napi::Persistent(func);
+    constructor.SuppressDestruct();
+
+    exports.Set("Client", func);
+    return exports;
+}
+
+Client::Client(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Client>(info) {
+    Napi::Env env = info.Env();
+    Napi::HandleScope scope(env);
+    Napi::Object clientConfig = info[0].As<Napi::Object>();
+
+    if (!clientConfig.Has(CFG_SERVICE_URL) || !clientConfig.Get(CFG_SERVICE_URL).IsString()) {
+        if (clientConfig.Get(CFG_SERVICE_URL).ToString().Utf8Value().empty()) {
+            Napi::Error::New(env, "Service URL is required and must be specified as a string")
+                .ThrowAsJavaScriptException();
+            return;
+        }
+    }
+    Napi::String serviceUrl = clientConfig.Get(CFG_SERVICE_URL).ToString();
+
+    pulsar_client_configuration_t *cClientConfig = pulsar_client_configuration_create();
+
+    if (clientConfig.Has(CFG_OP_TIMEOUT) && clientConfig.Get(CFG_OP_TIMEOUT).IsNumber()) {
+        int32_t operationTimeoutSeconds = clientConfig.Get(CFG_OP_TIMEOUT).ToNumber().Int32Value();
+        if (operationTimeoutSeconds > 0) {
+            pulsar_client_configuration_set_operation_timeout_seconds(cClientConfig, operationTimeoutSeconds);
+        }
+    }
+
+    if (clientConfig.Has(CFG_IO_THREADS) && clientConfig.Get(CFG_IO_THREADS).IsNumber()) {
+        int32_t ioThreads = clientConfig.Get(CFG_IO_THREADS).ToNumber().Int32Value();
+        if (ioThreads > 0) {
+            pulsar_client_configuration_set_io_threads(cClientConfig, ioThreads);
+        }
+    }
+
+    if (clientConfig.Has(CFG_LISTENER_THREADS) && clientConfig.Get(CFG_LISTENER_THREADS).IsNumber()) {
+        int32_t messageListenerThreads = clientConfig.Get(CFG_LISTENER_THREADS).ToNumber().Int32Value();
+        if (messageListenerThreads > 0) {
+            pulsar_client_configuration_set_message_listener_threads(cClientConfig, messageListenerThreads);
+        }
+    }
+
+    if (clientConfig.Has(CFG_CONCURRENT_LOOKUP) && clientConfig.Get(CFG_CONCURRENT_LOOKUP).IsNumber()) {
+        int32_t concurrentLookupRequest = clientConfig.Get(CFG_CONCURRENT_LOOKUP).ToNumber().Int32Value();
+        if (concurrentLookupRequest > 0) {
+            pulsar_client_configuration_set_concurrent_lookup_request(cClientConfig, concurrentLookupRequest);
+        }
+    }
+
+    if (clientConfig.Has(CFG_USE_TLS) && clientConfig.Get(CFG_USE_TLS).IsBoolean()) {
+        Napi::Boolean useTls = clientConfig.Get(CFG_USE_TLS).ToBoolean();
+        pulsar_client_configuration_set_use_tls(cClientConfig, useTls.Value());
+    }
+
+    if (clientConfig.Has(CFG_TLS_TRUST_CERT) && clientConfig.Get(CFG_TLS_TRUST_CERT).IsString()) {
+        Napi::String tlsTrustCertsFilePath = clientConfig.Get(CFG_TLS_TRUST_CERT).ToString();
+        pulsar_client_configuration_set_tls_trust_certs_file_path(cClientConfig,
+                                                                  tlsTrustCertsFilePath.Utf8Value().c_str());
+    }
+
+    if (clientConfig.Has(CFG_TLS_VALIDATE_HOSTNAME) &&
+        clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).IsBoolean()) {
+        Napi::Boolean tlsValidateHostname = clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).ToBoolean();
+        pulsar_client_configuration_set_validate_hostname(cClientConfig, tlsValidateHostname.Value());
+    }
+
+    if (clientConfig.Has(CFG_TLS_ALLOW_INSECURE) && clientConfig.Get(CFG_TLS_ALLOW_INSECURE).IsBoolean()) {
+        Napi::Boolean tlsAllowInsecureConnection = clientConfig.Get(CFG_TLS_ALLOW_INSECURE).ToBoolean();
+        pulsar_client_configuration_set_tls_allow_insecure_connection(cClientConfig,
+                                                                      tlsAllowInsecureConnection.Value());
+    }
+
+    if (clientConfig.Has(CFG_STATS_INTERVAL) && clientConfig.Get(CFG_STATS_INTERVAL).IsNumber()) {
+        uint32_t statsIntervalInSeconds = clientConfig.Get(CFG_STATS_INTERVAL).ToNumber().Uint32Value();
+        if (statsIntervalInSeconds > 0) {
+            pulsar_client_configuration_set_stats_interval_in_seconds(cClientConfig, statsIntervalInSeconds);
+        }
+    }
+
+    this->cClient = pulsar_client_create(serviceUrl.Utf8Value().c_str(), cClientConfig);
+    pulsar_client_configuration_free(cClientConfig);
+}
+
+Client::~Client() { pulsar_client_free(this->cClient); }
+
+Napi::Value Client::CreateProducer(const Napi::CallbackInfo &info) {
+    return Producer::NewInstance(info, this->cClient);
+}
+
+Napi::Value Client::Subscribe(const Napi::CallbackInfo &info) {
+    return Consumer::NewInstance(info, this->cClient);
+}
+
+class ClientCloseWorker : public Napi::AsyncWorker {
+   public:
+    ClientCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient)
+        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+          deferred(deferred),
+          cClient(cClient) {}
+    ~ClientCloseWorker() {}
+    void Execute() {
+        pulsar_result result = pulsar_client_close(this->cClient);
+        if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+    }
+    void OnOK() { this->deferred.Resolve(Env().Null()); }
+    void OnError(const Napi::Error &e) {
+        this->deferred.Reject(
+            Napi::Error::New(Env(), std::string("Failed to close client: ") + e.Message()).Value());
+    }
+
+   private:
+    Napi::Promise::Deferred deferred;
+    pulsar_client_t *cClient;
+};
+
+Napi::Value Client::Close(const Napi::CallbackInfo &info) {
+    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+    ClientCloseWorker *wk = new ClientCloseWorker(deferred, this->cClient);
+    wk->Queue();
+    return deferred.Promise();
+}
diff --git a/src/Client.h b/src/Client.h
new file mode 100644
index 0000000..bc92125
--- /dev/null
+++ b/src/Client.h
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include <napi.h>
+#include <pulsar/c/client.h>
+
+class Client : public Napi::ObjectWrap<Client> {
+   public:
+    static Napi::Object Init(Napi::Env env, Napi::Object exports);
+    Client(const Napi::CallbackInfo &info);
+    ~Client();
+
+   private:
+    static Napi::FunctionReference constructor;
+    pulsar_client_t *cClient;
+
+    Napi::Value CreateProducer(const Napi::CallbackInfo &info);
+    Napi::Value Subscribe(const Napi::CallbackInfo &info);
+    Napi::Value Close(const Napi::CallbackInfo &info);
+};
+
+#endif
diff --git a/src/Consumer.cc b/src/Consumer.cc
new file mode 100644
index 0000000..85e0fbf
--- /dev/null
+++ b/src/Consumer.cc
@@ -0,0 +1,188 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "Consumer.h"
+#include "ConsumerConfig.h"
+#include "Message.h"
+#include "MessageId.h"
+#include <pulsar/c/result.h>
+
+Napi::FunctionReference Consumer::constructor;
+
+void Consumer::Init(Napi::Env env, Napi::Object exports) {
+    Napi::HandleScope scope(env);
+
+    Napi::Function func = DefineClass(env, "Consumer",
+                                      {
+                                          InstanceMethod("receive", &Consumer::Receive),
+                                          InstanceMethod("acknowledge", &Consumer::Acknowledge),
+                                          InstanceMethod("acknowledgeId", &Consumer::AcknowledgeId),
+                                          InstanceMethod("close", &Consumer::Close),
+                                      });
+
+    constructor = Napi::Persistent(func);
+    constructor.SuppressDestruct();
+}
+
+void Consumer::SetCConsumer(pulsar_consumer_t *cConsumer) { this->cConsumer = cConsumer; }
+
+Consumer::Consumer(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Consumer>(info) {}
+
+class ConsumerNewInstanceWorker : public Napi::AsyncWorker {
+   public:
+    ConsumerNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
+                              ConsumerConfig *consumerConfig)
+        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+          deferred(deferred),
+          cClient(cClient),
+          consumerConfig(consumerConfig) {}
+    ~ConsumerNewInstanceWorker() {}
+    void Execute() {
+        const std::string &topic = this->consumerConfig->GetTopic();
+        if (topic.empty()) {
+            SetError(
+                std::string("Topic is required and must be specified as a string when creating consumer"));
+            return;
+        }
+        const std::string &subscription = this->consumerConfig->GetSubscription();
+        if (subscription.empty()) {
+            SetError(std::string(
+                "Subscription is required and must be specified as a string when creating consumer"));
+            return;
+        }
+        int32_t ackTimeoutMs = this->consumerConfig->GetAckTimeoutMs();
+        if (ackTimeoutMs != 0 && ackTimeoutMs < MIN_ACK_TIMEOUT_MILLIS) {
+            std::string msg("Ack timeout should be 0 or greater than or equal to " +
+                            std::to_string(MIN_ACK_TIMEOUT_MILLIS));
+            SetError(msg);
+            return;
+        }
+
+        pulsar_result result =
+            pulsar_client_subscribe(this->cClient, topic.c_str(), subscription.c_str(),
+                                    this->consumerConfig->GetCConsumerConfig(), &(this->cConsumer));
+        delete this->consumerConfig;
+        if (result != pulsar_result_Ok) {
+            SetError(std::string("Failed to create consumer: ") + pulsar_result_str(result));
+            return;
+        }
+    }
+    void OnOK() {
+        Napi::Object obj = Consumer::constructor.New({});
+        Consumer *consumer = Consumer::Unwrap(obj);
+        consumer->SetCConsumer(this->cConsumer);
+        this->deferred.Resolve(obj);
+    }
+    void OnError(const Napi::Error &e) {
+        this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value());
+    }
+
+   private:
+    Napi::Promise::Deferred deferred;
+    pulsar_client_t *cClient;
+    ConsumerConfig *consumerConfig;
+    pulsar_consumer_t *cConsumer;
+};
+
+Napi::Value Consumer::NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient) {
+    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+    Napi::Object config = info[0].As<Napi::Object>();
+    ConsumerConfig *consumerConfig = new ConsumerConfig(config);
+    ConsumerNewInstanceWorker *wk = new ConsumerNewInstanceWorker(deferred, cClient, consumerConfig);
+    wk->Queue();
+    return deferred.Promise();
+}
+
+class ConsumerReceiveWorker : public Napi::AsyncWorker {
+   public:
+    ConsumerReceiveWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer)
+        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+          deferred(deferred),
+          cConsumer(cConsumer) {}
+    ~ConsumerReceiveWorker() {}
+    void Execute() {
+        pulsar_result result = pulsar_consumer_receive(this->cConsumer, &(this->cMessage));
+
+        if (result != pulsar_result_Ok) {
+            SetError(std::string("Failed to received message ") + pulsar_result_str(result));
+        }
+    }
+    void OnOK() {
+        Napi::Object obj = Message::NewInstance({}, this->cMessage);
+        this->deferred.Resolve(obj);
+    }
+    void OnError(const Napi::Error &e) {
+        this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value());
+    }
+
+   private:
+    Napi::Promise::Deferred deferred;
+    pulsar_consumer_t *cConsumer;
+    pulsar_message_t *cMessage;
+};
+
+Napi::Value Consumer::Receive(const Napi::CallbackInfo &info) {
+    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+    ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->cConsumer);
+    wk->Queue();
+    return deferred.Promise();
+}
+
+void Consumer::Acknowledge(const Napi::CallbackInfo &info) {
+    Napi::Object obj = info[0].As<Napi::Object>();
+    Message *msg = Message::Unwrap(obj);
+    pulsar_consumer_acknowledge_async(this->cConsumer, msg->GetCMessage(), NULL, NULL);
+}
+
+void Consumer::AcknowledgeId(const Napi::CallbackInfo &info) {
+    Napi::Object obj = info[0].As<Napi::Object>();
+    MessageId *msgId = MessageId::Unwrap(obj);
+    pulsar_consumer_acknowledge_async_id(this->cConsumer, msgId->GetCMessageId(), NULL, NULL);
+}
+
+class ConsumerCloseWorker : public Napi::AsyncWorker {
+   public:
+    ConsumerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer)
+        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+          deferred(deferred),
+          cConsumer(cConsumer) {}
+    ~ConsumerCloseWorker() {}
+    void Execute() {
+        pulsar_result result = pulsar_consumer_close(this->cConsumer);
+        if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+    }
+    void OnOK() { this->deferred.Resolve(Env().Null()); }
+    void OnError(const Napi::Error &e) {
+        this->deferred.Reject(
+            Napi::Error::New(Env(), std::string("Failed to close consumer: ") + e.Message()).Value());
+    }
+
+   private:
+    Napi::Promise::Deferred deferred;
+    pulsar_consumer_t *cConsumer;
+};
+
+Napi::Value Consumer::Close(const Napi::CallbackInfo &info) {
+    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+    ConsumerCloseWorker *wk = new ConsumerCloseWorker(deferred, this->cConsumer);
+    wk->Queue();
+    return deferred.Promise();
+}
+
+Consumer::~Consumer() { pulsar_consumer_free(this->cConsumer); }
diff --git a/src/Consumer.h b/src/Consumer.h
new file mode 100644
index 0000000..8e77df3
--- /dev/null
+++ b/src/Consumer.h
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef CONSUMER_H
+#define CONSUMER_H
+
+#include <napi.h>
+#include <pulsar/c/client.h>
+
+class Consumer : public Napi::ObjectWrap<Consumer> {
+   public:
+    static void Init(Napi::Env env, Napi::Object exports);
+    static Napi::Value NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient);
+    static Napi::FunctionReference constructor;
+    Consumer(const Napi::CallbackInfo &info);
+    ~Consumer();
+    void SetCConsumer(pulsar_consumer_t *cConsumer);
+
+   private:
+    pulsar_consumer_t *cConsumer;
+
+    Napi::Value Receive(const Napi::CallbackInfo &info);
+    void Acknowledge(const Napi::CallbackInfo &info);
+    void AcknowledgeId(const Napi::CallbackInfo &info);
+    Napi::Value Close(const Napi::CallbackInfo &info);
+};
+
+#endif
diff --git a/src/ConsumerConfig.cc b/src/ConsumerConfig.cc
new file mode 100644
index 0000000..78f1605
--- /dev/null
+++ b/src/ConsumerConfig.cc
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "ConsumerConfig.h"
+#include <pulsar/c/consumer_configuration.h>
+#include <map>
+
+static const std::string CFG_TOPIC = "topic";
+static const std::string CFG_SUBSCRIPTION = "subscription";
+static const std::string CFG_SUBSCRIPTION_TYPE = "subscriptionType";
+static const std::string CFG_ACK_TIMEOUT = "ackTimeoutMs";
+static const std::string CFG_RECV_QUEUE = "receiverQueueSize";
+static const std::string CFG_RECV_QUEUE_ACROSS_PARTITIONS = "receiverQueueSizeAcrossPartitions";
+static const std::string CFG_CONSUMER_NAME = "consumerName";
+static const std::string CFG_PROPS = "properties";
+
+static const std::map<std::string, pulsar_consumer_type> SUBSCRIPTION_TYPE = {
+    {"Exclusive", pulsar_ConsumerExclusive},
+    {"Shared", pulsar_ConsumerShared},
+    {"Failover", pulsar_ConsumerFailover}};
+
+ConsumerConfig::ConsumerConfig(const Napi::Object &consumerConfig)
+    : topic(""), subscription(""), ackTimeoutMs(0) {
+    this->cConsumerConfig = pulsar_consumer_configuration_create();
+
+    if (consumerConfig.Has(CFG_TOPIC) && consumerConfig.Get(CFG_TOPIC).IsString()) {
+        this->topic = consumerConfig.Get(CFG_TOPIC).ToString().Utf8Value();
+    }
+
+    if (consumerConfig.Has(CFG_SUBSCRIPTION) && consumerConfig.Get(CFG_SUBSCRIPTION).IsString()) {
+        this->subscription = consumerConfig.Get(CFG_SUBSCRIPTION).ToString().Utf8Value();
+    }
+
+    if (consumerConfig.Has(CFG_SUBSCRIPTION_TYPE) && consumerConfig.Get(CFG_SUBSCRIPTION_TYPE).IsString()) {
+        std::string subscriptionType = consumerConfig.Get(CFG_SUBSCRIPTION_TYPE).ToString().Utf8Value();
+        if (SUBSCRIPTION_TYPE.count(subscriptionType)) {
+            pulsar_consumer_configuration_set_consumer_type(this->cConsumerConfig,
+                                                            SUBSCRIPTION_TYPE.at(subscriptionType));
+        }
+    }
+
+    if (consumerConfig.Has(CFG_CONSUMER_NAME) && consumerConfig.Get(CFG_CONSUMER_NAME).IsString()) {
+        std::string consumerName = consumerConfig.Get(CFG_CONSUMER_NAME).ToString().Utf8Value();
+        if (!consumerName.empty())
+            pulsar_consumer_set_consumer_name(this->cConsumerConfig, consumerName.c_str());
+    }
+
+    if (consumerConfig.Has(CFG_ACK_TIMEOUT) && consumerConfig.Get(CFG_ACK_TIMEOUT).IsNumber()) {
+        this->ackTimeoutMs = consumerConfig.Get(CFG_ACK_TIMEOUT).ToNumber().Int64Value();
+        if (this->ackTimeoutMs == 0 || this->ackTimeoutMs >= MIN_ACK_TIMEOUT_MILLIS) {
+            pulsar_consumer_set_unacked_messages_timeout_ms(this->cConsumerConfig, this->ackTimeoutMs);
+        }
+    }
+
+    if (consumerConfig.Has(CFG_RECV_QUEUE) && consumerConfig.Get(CFG_RECV_QUEUE).IsNumber()) {
+        int32_t receiverQueueSize = consumerConfig.Get(CFG_RECV_QUEUE).ToNumber().Int32Value();
+        if (receiverQueueSize >= 0) {
+            pulsar_consumer_configuration_set_receiver_queue_size(this->cConsumerConfig, receiverQueueSize);
+        }
+    }
+
+    if (consumerConfig.Has(CFG_RECV_QUEUE_ACROSS_PARTITIONS) &&
+        consumerConfig.Get(CFG_RECV_QUEUE_ACROSS_PARTITIONS).IsNumber()) {
+        int32_t receiverQueueSizeAcrossPartitions =
+            consumerConfig.Get(CFG_RECV_QUEUE_ACROSS_PARTITIONS).ToNumber().Int32Value();
+        if (receiverQueueSizeAcrossPartitions >= 0) {
+            pulsar_consumer_set_max_total_receiver_queue_size_across_partitions(
+                this->cConsumerConfig, receiverQueueSizeAcrossPartitions);
+        }
+    }
+
+    if (consumerConfig.Has(CFG_PROPS) && consumerConfig.Get(CFG_PROPS).IsObject()) {
+        Napi::Object propObj = consumerConfig.Get(CFG_PROPS).ToObject();
+        Napi::Array arr = propObj.GetPropertyNames();
+        int size = arr.Length();
+        for (int i = 0; i < size; i++) {
+            std::string key = arr.Get(i).ToString().Utf8Value();
+            std::string value = propObj.Get(key).ToString().Utf8Value();
+            pulsar_consumer_configuration_set_property(this->cConsumerConfig, key.c_str(), value.c_str());
+        }
+    }
+}
+
+ConsumerConfig::~ConsumerConfig() { pulsar_consumer_configuration_free(this->cConsumerConfig); }
+
+pulsar_consumer_configuration_t *ConsumerConfig::GetCConsumerConfig() { return this->cConsumerConfig; }
+
+std::string ConsumerConfig::GetTopic() { return this->topic; }
+std::string ConsumerConfig::GetSubscription() { return this->subscription; }
+int64_t ConsumerConfig::GetAckTimeoutMs() { return this->ackTimeoutMs; }
diff --git a/src/ConsumerConfig.h b/src/ConsumerConfig.h
new file mode 100644
index 0000000..00c890e
--- /dev/null
+++ b/src/ConsumerConfig.h
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef CONSUMER_CONFIG_H
+#define CONSUMER_CONFIG_H
+
+#include <napi.h>
+#include <pulsar/c/consumer_configuration.h>
+
+#define MIN_ACK_TIMEOUT_MILLIS 10000
+
+class ConsumerConfig {
+   public:
+    ConsumerConfig(const Napi::Object &consumerConfig);
+    ~ConsumerConfig();
+    pulsar_consumer_configuration_t *GetCConsumerConfig();
+    std::string GetTopic();
+    std::string GetSubscription();
+    int64_t GetAckTimeoutMs();
+
+   private:
+    pulsar_consumer_configuration_t *cConsumerConfig;
+    std::string topic;
+    std::string subscription;
+    int64_t ackTimeoutMs;
+};
+
+#endif
diff --git a/src/Message.cc b/src/Message.cc
new file mode 100644
index 0000000..9a0129e
--- /dev/null
+++ b/src/Message.cc
@@ -0,0 +1,197 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "Message.h"
+#include "MessageId.h"
+#include <pulsar/c/message.h>
+
+static const std::string CFG_DATA = "data";
+static const std::string CFG_PROPS = "properties";
+static const std::string CFG_EVENT_TIME = "eventTimestamp";
+static const std::string CFG_SEQUENCE_ID = "sequenceId";
+static const std::string CFG_PARTITION_KEY = "partitionKey";
+static const std::string CFG_REPL_CLUSTERS = "replicationClusters";
+
+Napi::FunctionReference Message::constructor;
+
+Napi::Object Message::Init(Napi::Env env, Napi::Object exports) {
+    Napi::HandleScope scope(env);
+
+    Napi::Function func = DefineClass(
+        env, "Message",
+        {InstanceMethod("getTopicName", &Message::GetTopicName),
+         InstanceMethod("getProperties", &Message::GetProperties),
+         InstanceMethod("getData", &Message::GetData), InstanceMethod("getMessageId", &Message::GetMessageId),
+         InstanceMethod("getPublishTimestamp", &Message::GetPublishTimestamp),
+         InstanceMethod("getEventTimestamp", &Message::GetEventTimestamp),
+         InstanceMethod("getPartitionKey", &Message::GetPartitionKey)});
+
+    constructor = Napi::Persistent(func);
+    constructor.SuppressDestruct();
+
+    exports.Set("Message", func);
+    return exports;
+}
+
+Napi::Object Message::NewInstance(Napi::Value arg, pulsar_message_t *cMessage) {
+    Napi::Object obj = constructor.New({});
+    Message *msg = Unwrap(obj);
+    msg->cMessage = cMessage;
+    return obj;
+}
+
+Message::Message(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Message>(info), cMessage(nullptr) {}
+
+pulsar_message_t *Message::GetCMessage() { return this->cMessage; }
+
+Napi::Value Message::GetTopicName(const Napi::CallbackInfo &info) {
+    Napi::Env env = info.Env();
+    if (!ValidateCMessage(env)) {
+        return env.Null();
+    }
+    return Napi::String::New(env, pulsar_message_get_topic_name(this->cMessage));
+}
+
+Napi::Value Message::GetProperties(const Napi::CallbackInfo &info) {
+    Napi::Env env = info.Env();
+    if (!ValidateCMessage(env)) {
+        return env.Null();
+    }
+    Napi::Array arr = Napi::Array::New(env);
+    pulsar_string_map_t *cProperties = pulsar_message_get_properties(this->cMessage);
+    int size = pulsar_string_map_size(cProperties);
+    for (int i = 0; i < size; i++) {
+        arr.Set(pulsar_string_map_get_key(cProperties, i), pulsar_string_map_get_value(cProperties, i));
+    }
+    return arr;
+}
+
+Napi::Value Message::GetData(const Napi::CallbackInfo &info) {
+    Napi::Env env = info.Env();
+    if (!ValidateCMessage(env)) {
+        return env.Null();
+    }
+    void *data = const_cast<void *>(pulsar_message_get_data(this->cMessage));
+    size_t size = (size_t)pulsar_message_get_length(this->cMessage);
+    return Napi::Buffer<char>::New(env, (char *)data, size);
+}
+
+Napi::Value Message::GetMessageId(const Napi::CallbackInfo &info) {
+    Napi::Env env = info.Env();
+    if (!ValidateCMessage(env)) {
+        return env.Null();
+    }
+    return MessageId::NewInstanceFromMessage(info, this->cMessage);
+}
+
+Napi::Value Message::GetEventTimestamp(const Napi::CallbackInfo &info) {
+    Napi::Env env = info.Env();
+    if (!ValidateCMessage(env)) {
+        return env.Null();
+    }
+    return Napi::Number::New(env, pulsar_message_get_event_timestamp(this->cMessage));
+}
+
+Napi::Value Message::GetPublishTimestamp(const Napi::CallbackInfo &info) {
+    Napi::Env env = info.Env();
+    if (!ValidateCMessage(env)) {
+        return env.Null();
+    }
+    return Napi::Number::New(env, pulsar_message_get_publish_timestamp(this->cMessage));
+}
+
+Napi::Value Message::GetPartitionKey(const Napi::CallbackInfo &info) {
+    Napi::Env env = info.Env();
+    if (!ValidateCMessage(env)) {
+        return env.Null();
+    }
+    return Napi::String::New(env, pulsar_message_get_partitionKey(this->cMessage));
+}
+
+bool Message::ValidateCMessage(Napi::Env env) {
+    if (this->cMessage) {
+        return true;
+    } else {
+        Napi::Error::New(env, "Message has not been built").ThrowAsJavaScriptException();
+        return false;
+    }
+}
+
+pulsar_message_t *Message::BuildMessage(Napi::Object conf) {
+    pulsar_message_t *cMessage = pulsar_message_create();
+
+    if (conf.Has(CFG_DATA) && conf.Get(CFG_DATA).IsBuffer()) {
+        Napi::Buffer<char> buf = conf.Get(CFG_DATA).As<Napi::Buffer<char>>();
+        char *data = buf.Data();
+        pulsar_message_set_content(cMessage, data, buf.Length());
+    }
+
+    if (conf.Has(CFG_PROPS) && conf.Get(CFG_PROPS).IsObject()) {
+        Napi::Object propObj = conf.Get(CFG_PROPS).ToObject();
+        Napi::Array arr = propObj.GetPropertyNames();
+        int size = arr.Length();
+        for (int i = 0; i < size; i++) {
+            Napi::String key = arr.Get(i).ToString();
+            Napi::String value = propObj.Get(key).ToString();
+            pulsar_message_set_property(cMessage, key.Utf8Value().c_str(), value.Utf8Value().c_str());
+        }
+    }
+
+    if (conf.Has(CFG_EVENT_TIME) && conf.Get(CFG_EVENT_TIME).IsNumber()) {
+        int64_t eventTimestamp = conf.Get(CFG_EVENT_TIME).ToNumber().Int64Value();
+        if (eventTimestamp >= 0) {
+            pulsar_message_set_event_timestamp(cMessage, eventTimestamp);
+        }
+    }
+
+    if (conf.Has(CFG_SEQUENCE_ID) && conf.Get(CFG_SEQUENCE_ID).IsNumber()) {
+        Napi::Number sequenceId = conf.Get(CFG_SEQUENCE_ID).ToNumber();
+        pulsar_message_set_sequence_id(cMessage, sequenceId.Int64Value());
+    }
+
+    if (conf.Has(CFG_PARTITION_KEY) && conf.Get(CFG_PARTITION_KEY).IsString()) {
+        Napi::String partitionKey = conf.Get(CFG_PARTITION_KEY).ToString();
+        pulsar_message_set_partition_key(cMessage, partitionKey.Utf8Value().c_str());
+    }
+
+    if (conf.Has(CFG_REPL_CLUSTERS) && conf.Get(CFG_REPL_CLUSTERS).IsArray()) {
+        Napi::Array clusters = conf.Get(CFG_REPL_CLUSTERS).As<Napi::Array>();
+        // Empty list means to disable replication
+        int length = clusters.Length();
+        if (length == 0) {
+            pulsar_message_disable_replication(cMessage, 1);
+        } else {
+            char **arr = NewStringArray(length);
+            for (int i = 0; i < length; i++) {
+                SetString(arr, clusters.Get(i).ToString().Utf8Value().c_str(), i);
+            }
+            // TODO: temoporalily commented out unless 2.3.1 which includes interface change of
+            // pulsar_message_set_replication_clusters (#3729) is released
+            // pulsar_message_set_replication_clusters(cMessage, (const char **)arr, length);
+            FreeStringArray(arr, length);
+        }
+    }
+    return cMessage;
+}
+
+Message::~Message() {
+    if (this->cMessage != nullptr) {
+        pulsar_message_free(this->cMessage);
+    }
+}
diff --git a/src/Message.h b/src/Message.h
new file mode 100644
index 0000000..8992f9c
--- /dev/null
+++ b/src/Message.h
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#include <napi.h>
+#include <pulsar/c/message.h>
+
+class Message : public Napi::ObjectWrap<Message> {
+   public:
+    static Napi::Object Init(Napi::Env env, Napi::Object exports);
+    static Napi::Object NewInstance(Napi::Value arg, pulsar_message_t *cMessage);
+    static pulsar_message_t *BuildMessage(Napi::Object conf);
+    Message(const Napi::CallbackInfo &info);
+    ~Message();
+    pulsar_message_t *GetCMessage();
+
+   private:
+    static Napi::FunctionReference constructor;
+
+    pulsar_message_t *cMessage;
+
+    Napi::Value GetTopicName(const Napi::CallbackInfo &info);
+    Napi::Value GetProperties(const Napi::CallbackInfo &info);
+    Napi::Value GetData(const Napi::CallbackInfo &info);
+    Napi::Value GetMessageId(const Napi::CallbackInfo &info);
+    Napi::Value GetPublishTimestamp(const Napi::CallbackInfo &info);
+    Napi::Value GetEventTimestamp(const Napi::CallbackInfo &info);
+    Napi::Value GetPartitionKey(const Napi::CallbackInfo &info);
+    bool ValidateCMessage(Napi::Env env);
+
+    static char **NewStringArray(int size) { return (char **)calloc(sizeof(char *), size); }
+    static void SetString(char **array, const char *str, int n) {
+        char *copied = (char *)calloc(strlen(str) + 1, sizeof(char));
+        strcpy(copied, str);
+        array[n] = copied;
+    }
+    static void FreeStringArray(char **array, int size) {
+        int i;
+        for (i = 0; i < size; i++) {
+            free(array[i]);
+        }
+        free(array);
+    }
+};
+
+#endif
diff --git a/src/MessageId.cc b/src/MessageId.cc
new file mode 100644
index 0000000..3a95f43
--- /dev/null
+++ b/src/MessageId.cc
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "MessageId.h"
+#include <pulsar/c/message.h>
+#include <pulsar/c/message_id.h>
+
+Napi::FunctionReference MessageId::constructor;
+
+Napi::Object MessageId::Init(Napi::Env env, Napi::Object exports) {
+    Napi::HandleScope scope(env);
+
+    Napi::Function func = DefineClass(env, "MessageId",
+                                      {
+                                          StaticMethod("earliest", &MessageId::Earliest, napi_static),
+                                          StaticMethod("latest", &MessageId::Latest, napi_static),
+                                          StaticMethod("finalize", &MessageId::Finalize, napi_static),
+                                          InstanceMethod("toString", &MessageId::ToString),
+                                      });
+
+    constructor = Napi::Persistent(func);
+    constructor.SuppressDestruct();
+
+    exports.Set("MessageId", func);
+    return exports;
+}
+
+MessageId::MessageId(const Napi::CallbackInfo &info) : Napi::ObjectWrap<MessageId>(info) {
+    Napi::Env env = info.Env();
+    Napi::HandleScope scope(env);
+}
+
+Napi::Object MessageId::NewInstanceFromMessage(const Napi::CallbackInfo &info, pulsar_message_t *cMessage) {
+    Napi::Object obj = NewInstance(info[0]);
+    MessageId *msgId = Unwrap(obj);
+    msgId->cMessageId = pulsar_message_get_message_id(cMessage);
+    return obj;
+}
+
+Napi::Object MessageId::NewInstance(Napi::Value arg) {
+    Napi::Object obj = constructor.New({arg});
+    return obj;
+}
+
+void MessageId::Finalize(const Napi::CallbackInfo &info) {
+    Napi::Object obj = info[0].As<Napi::Object>();
+    MessageId *msgId = Unwrap(obj);
+    pulsar_message_id_free(msgId->cMessageId);
+}
+
+Napi::Value MessageId::Earliest(const Napi::CallbackInfo &info) {
+    Napi::Object obj = NewInstance(info[0]);
+    MessageId *msgId = Unwrap(obj);
+    msgId->cMessageId = (pulsar_message_id_t *)pulsar_message_id_earliest();
+    return obj;
+}
+
+Napi::Value MessageId::Latest(const Napi::CallbackInfo &info) {
+    Napi::Object obj = NewInstance(info[0]);
+    MessageId *msgId = Unwrap(obj);
+    msgId->cMessageId = (pulsar_message_id_t *)pulsar_message_id_latest();
+    return obj;
+}
+
+pulsar_message_id_t *MessageId::GetCMessageId() { return this->cMessageId; }
+
+Napi::Value MessageId::ToString(const Napi::CallbackInfo &info) {
+    return Napi::String::New(info.Env(), pulsar_message_id_str(this->cMessageId));
+}
+
+MessageId::~MessageId() { pulsar_message_id_free(this->cMessageId); }
diff --git a/src/MessageId.h b/src/MessageId.h
new file mode 100644
index 0000000..761f04b
--- /dev/null
+++ b/src/MessageId.h
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef MESSAGE_ID_H
+#define MESSAGE_ID_H
+
+#include <napi.h>
+#include <pulsar/c/message.h>
+#include <pulsar/c/message_id.h>
+
+class MessageId : public Napi::ObjectWrap<MessageId> {
+   public:
+    static Napi::Object Init(Napi::Env env, Napi::Object exports);
+    static Napi::Object NewInstance(Napi::Value arg);
+    static Napi::Object NewInstanceFromMessage(const Napi::CallbackInfo &info, pulsar_message_t *cMessage);
+    static Napi::Value Earliest(const Napi::CallbackInfo &info);
+    static Napi::Value Latest(const Napi::CallbackInfo &info);
+    static void Finalize(const Napi::CallbackInfo &info);
+    MessageId(const Napi::CallbackInfo &info);
+    ~MessageId();
+    pulsar_message_id_t *GetCMessageId();
+
+   private:
+    static Napi::FunctionReference constructor;
+    pulsar_message_id_t *cMessageId;
+
+    Napi::Value ToString(const Napi::CallbackInfo &info);
+};
+
+#endif
diff --git a/src/Producer.cc b/src/Producer.cc
new file mode 100644
index 0000000..9bdb5d4
--- /dev/null
+++ b/src/Producer.cc
@@ -0,0 +1,155 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "Producer.h"
+#include "ProducerConfig.h"
+#include "Message.h"
+#include <pulsar/c/result.h>
+#include <memory>
+Napi::FunctionReference Producer::constructor;
+
+void Producer::Init(Napi::Env env, Napi::Object exports) {
+    Napi::HandleScope scope(env);
+
+    Napi::Function func =
+        DefineClass(env, "Producer",
+                    {InstanceMethod("send", &Producer::Send), InstanceMethod("close", &Producer::Close)});
+
+    constructor = Napi::Persistent(func);
+    constructor.SuppressDestruct();
+}
+
+void Producer::SetCProducer(pulsar_producer_t *cProducer) { this->cProducer = cProducer; }
+
+class ProducerNewInstanceWorker : public Napi::AsyncWorker {
+   public:
+    ProducerNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
+                              ProducerConfig *producerConfig)
+        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+          deferred(deferred),
+          cClient(cClient),
+          producerConfig(producerConfig) {}
+    ~ProducerNewInstanceWorker() {}
+    void Execute() {
+        const std::string &topic = this->producerConfig->GetTopic();
+        if (topic.empty()) {
+            SetError(
+                std::string("Topic is required and must be specified as a string when creating producer"));
+            return;
+        }
+
+        pulsar_result result = pulsar_client_create_producer(
+            this->cClient, topic.c_str(), this->producerConfig->GetCProducerConfig(), &(this->cProducer));
+        delete this->producerConfig;
+        if (result != pulsar_result_Ok) {
+            SetError(std::string("Failed to create producer: ") + pulsar_result_str(result));
+            return;
+        }
+    }
+    void OnOK() {
+        Napi::Object obj = Producer::constructor.New({});
+        Producer *producer = Producer::Unwrap(obj);
+        producer->SetCProducer(this->cProducer);
+        this->deferred.Resolve(obj);
+    }
+    void OnError(const Napi::Error &e) {
+        this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value());
+    }
+
+   private:
+    Napi::Promise::Deferred deferred;
+    pulsar_client_t *cClient;
+    ProducerConfig *producerConfig;
+    pulsar_producer_t *cProducer;
+};
+
+Napi::Value Producer::NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient) {
+    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+    Napi::Object config = info[0].As<Napi::Object>();
+    ProducerConfig *producerConfig = new ProducerConfig(config);
+    ProducerNewInstanceWorker *wk = new ProducerNewInstanceWorker(deferred, cClient, producerConfig);
+    wk->Queue();
+    return deferred.Promise();
+}
+
+Producer::Producer(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Producer>(info) {}
+
+class ProducerSendWorker : public Napi::AsyncWorker {
+   public:
+    ProducerSendWorker(const Napi::Promise::Deferred &deferred, pulsar_producer_t *cProducer,
+                       pulsar_message_t *cMessage)
+        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+          deferred(deferred),
+          cProducer(cProducer),
+          cMessage(cMessage) {}
+    ~ProducerSendWorker() { pulsar_message_free(this->cMessage); }
+    void Execute() {
+        pulsar_result result = pulsar_producer_send(this->cProducer, this->cMessage);
+        if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+    }
+    void OnOK() { this->deferred.Resolve(Env().Null()); }
+    void OnError(const Napi::Error &e) {
+        this->deferred.Reject(
+            Napi::Error::New(Env(), std::string("Failed to send message: ") + e.Message()).Value());
+    }
+
+   private:
+    Napi::Promise::Deferred deferred;
+    pulsar_producer_t *cProducer;
+    pulsar_message_t *cMessage;
+};
+
+Napi::Value Producer::Send(const Napi::CallbackInfo &info) {
+    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+    pulsar_message_t *cMessage = Message::BuildMessage(info[0].As<Napi::Object>());
+    ProducerSendWorker *wk = new ProducerSendWorker(deferred, this->cProducer, cMessage);
+    wk->Queue();
+    return deferred.Promise();
+}
+
+class ProducerCloseWorker : public Napi::AsyncWorker {
+   public:
+    ProducerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_producer_t *cProducer)
+        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+          deferred(deferred),
+          cProducer(cProducer) {}
+    ~ProducerCloseWorker() {}
+    void Execute() {
+        pulsar_result result = pulsar_producer_close(this->cProducer);
+        if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+    }
+    void OnOK() { this->deferred.Resolve(Env().Null()); }
+    void OnError(const Napi::Error &e) {
+        this->deferred.Reject(
+            Napi::Error::New(Env(), std::string("Failed to close producer: ") + e.Message()).Value());
+    }
+
+   private:
+    Napi::Promise::Deferred deferred;
+    pulsar_producer_t *cProducer;
+};
+
+Napi::Value Producer::Close(const Napi::CallbackInfo &info) {
+    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+    ProducerCloseWorker *wk = new ProducerCloseWorker(deferred, this->cProducer);
+    wk->Queue();
+    return deferred.Promise();
+}
+
+Producer::~Producer() { pulsar_producer_free(this->cProducer); }
diff --git a/src/Producer.h b/src/Producer.h
new file mode 100644
index 0000000..c758ce4
--- /dev/null
+++ b/src/Producer.h
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef PRODUCER_H
+#define PRODUCER_H
+
+#include <napi.h>
+#include <pulsar/c/client.h>
+#include <pulsar/c/producer.h>
+
+class Producer : public Napi::ObjectWrap<Producer> {
+   public:
+    static void Init(Napi::Env env, Napi::Object exports);
+    static Napi::Value NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient);
+    static Napi::FunctionReference constructor;
+    Producer(const Napi::CallbackInfo &info);
+    ~Producer();
+    void SetCProducer(pulsar_producer_t *cProducer);
+
+   private:
+    pulsar_producer_t *cProducer;
+    Napi::Value Send(const Napi::CallbackInfo &info);
+    Napi::Value Close(const Napi::CallbackInfo &info);
+};
+
+#endif
diff --git a/src/ProducerConfig.cc b/src/ProducerConfig.cc
new file mode 100644
index 0000000..9b94dd2
--- /dev/null
+++ b/src/ProducerConfig.cc
@@ -0,0 +1,159 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "ProducerConfig.h"
+#include <map>
+
+static const std::string CFG_TOPIC = "topic";
+static const std::string CFG_PRODUCER_NAME = "producerName";
+static const std::string CFG_SEND_TIMEOUT = "sendTimeoutMs";
+static const std::string CFG_INIT_SEQUENCE_ID = "initialSequenceId";
+static const std::string CFG_MAX_PENDING = "maxPendingMessages";
+static const std::string CFG_MAX_PENDING_ACROSS_PARTITIONS = "maxPendingMessagesAcrossPartitions";
+static const std::string CFG_BLOCK_IF_QUEUE_FULL = "blockIfQueueFull";
+static const std::string CFG_ROUTING_MODE = "messageRoutingMode";
+static const std::string CFG_HASH_SCHEME = "hashingScheme";
+static const std::string CFG_COMPRESS_TYPE = "compressionType";
+static const std::string CFG_BATCH_ENABLED = "batchingEnabled";
+static const std::string CFG_BATCH_MAX_DELAY = "batchingMaxPublishDelayMs";
+static const std::string CFG_BATCH_MAX_MSG = "batchingMaxMessages";
+static const std::string CFG_PROPS = "properties";
+
+static const std::map<std::string, pulsar_partitions_routing_mode> MESSAGE_ROUTING_MODE = {
+    {"UseSinglePartition", pulsar_UseSinglePartition},
+    {"RoundRobinDistribution", pulsar_RoundRobinDistribution},
+    {"CustomPartition", pulsar_CustomPartition}};
+
+static const std::map<std::string, pulsar_hashing_scheme> HASHING_SCHEME = {
+    {"Murmur3_32Hash", pulsar_Murmur3_32Hash},
+    {"BoostHash", pulsar_BoostHash},
+    {"JavaStringHash", pulsar_JavaStringHash},
+};
+
+static std::map<std::string, pulsar_compression_type> COMPRESSION_TYPE = {{"Zlib", pulsar_CompressionZLib},
+                                                                          {"LZ4", pulsar_CompressionLZ4}};
+
+ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
+    this->cProducerConfig = pulsar_producer_configuration_create();
+
+    if (producerConfig.Has(CFG_TOPIC) && producerConfig.Get(CFG_TOPIC).IsString()) {
+        this->topic = producerConfig.Get(CFG_TOPIC).ToString().Utf8Value();
+    }
+
+    if (producerConfig.Has(CFG_PRODUCER_NAME) && producerConfig.Get(CFG_PRODUCER_NAME).IsString()) {
+        std::string producerName = producerConfig.Get(CFG_PRODUCER_NAME).ToString().Utf8Value();
+        if (!producerName.empty())
+            pulsar_producer_configuration_set_producer_name(this->cProducerConfig, producerName.c_str());
+    }
+
+    if (producerConfig.Has(CFG_SEND_TIMEOUT) && producerConfig.Get(CFG_SEND_TIMEOUT).IsNumber()) {
+        int32_t sendTimeoutMs = producerConfig.Get(CFG_SEND_TIMEOUT).ToNumber().Int32Value();
+        if (sendTimeoutMs > 0) {
+            pulsar_producer_configuration_set_send_timeout(this->cProducerConfig, sendTimeoutMs);
+        }
+    }
+
+    if (producerConfig.Has(CFG_INIT_SEQUENCE_ID) && producerConfig.Get(CFG_INIT_SEQUENCE_ID).IsNumber()) {
+        int64_t initialSequenceId = producerConfig.Get(CFG_INIT_SEQUENCE_ID).ToNumber().Int64Value();
+        pulsar_producer_configuration_set_initial_sequence_id(this->cProducerConfig, initialSequenceId);
+    }
+
+    if (producerConfig.Has(CFG_MAX_PENDING) && producerConfig.Get(CFG_MAX_PENDING).IsNumber()) {
+        int32_t maxPendingMessages = producerConfig.Get(CFG_MAX_PENDING).ToNumber().Int32Value();
+        if (maxPendingMessages > 0) {
+            pulsar_producer_configuration_set_max_pending_messages(this->cProducerConfig, maxPendingMessages);
+        }
+    }
+
+    if (producerConfig.Has(CFG_MAX_PENDING_ACROSS_PARTITIONS) &&
+        producerConfig.Get(CFG_MAX_PENDING_ACROSS_PARTITIONS).IsNumber()) {
+        int32_t maxPendingMessagesAcrossPartitions =
+            producerConfig.Get(CFG_MAX_PENDING_ACROSS_PARTITIONS).ToNumber().Int32Value();
+        if (maxPendingMessagesAcrossPartitions > 0) {
+            pulsar_producer_configuration_set_max_pending_messages(this->cProducerConfig,
+                                                                   maxPendingMessagesAcrossPartitions);
+        }
+    }
+
+    if (producerConfig.Has(CFG_BLOCK_IF_QUEUE_FULL) &&
+        producerConfig.Get(CFG_BLOCK_IF_QUEUE_FULL).IsBoolean()) {
+        bool blockIfQueueFull = producerConfig.Get(CFG_BLOCK_IF_QUEUE_FULL).ToBoolean().Value();
+        pulsar_producer_configuration_set_block_if_queue_full(this->cProducerConfig, blockIfQueueFull);
+    }
+
+    if (producerConfig.Has(CFG_ROUTING_MODE) && producerConfig.Get(CFG_ROUTING_MODE).IsString()) {
+        std::string messageRoutingMode = producerConfig.Get(CFG_ROUTING_MODE).ToString().Utf8Value();
+        if (MESSAGE_ROUTING_MODE.count(messageRoutingMode))
+            pulsar_producer_configuration_set_partitions_routing_mode(
+                this->cProducerConfig, MESSAGE_ROUTING_MODE.at(messageRoutingMode));
+    }
+
+    if (producerConfig.Has(CFG_HASH_SCHEME) && producerConfig.Get(CFG_HASH_SCHEME).IsString()) {
+        std::string hashingScheme = producerConfig.Get(CFG_HASH_SCHEME).ToString().Utf8Value();
+        if (HASHING_SCHEME.count(hashingScheme))
+            pulsar_producer_configuration_set_hashing_scheme(this->cProducerConfig,
+                                                             HASHING_SCHEME.at(hashingScheme));
+    }
+
+    if (producerConfig.Has(CFG_COMPRESS_TYPE) && producerConfig.Get(CFG_COMPRESS_TYPE).IsString()) {
+        std::string compressionType = producerConfig.Get(CFG_COMPRESS_TYPE).ToString().Utf8Value();
+        if (COMPRESSION_TYPE.count(compressionType))
+            pulsar_producer_configuration_set_compression_type(this->cProducerConfig,
+                                                               COMPRESSION_TYPE.at(compressionType));
+    }
+
+    if (producerConfig.Has(CFG_BATCH_ENABLED) && producerConfig.Get(CFG_BATCH_ENABLED).IsBoolean()) {
+        bool batchingEnabled = producerConfig.Get(CFG_BATCH_ENABLED).ToBoolean().Value();
+        pulsar_producer_configuration_set_batching_enabled(this->cProducerConfig, batchingEnabled);
+    }
+
+    if (producerConfig.Has(CFG_BATCH_MAX_DELAY) && producerConfig.Get(CFG_BATCH_MAX_DELAY).IsNumber()) {
+        int64_t batchingMaxPublishDelayMs = producerConfig.Get(CFG_BATCH_MAX_DELAY).ToNumber().Int64Value();
+        if (batchingMaxPublishDelayMs > 0) {
+            pulsar_producer_configuration_set_batching_max_publish_delay_ms(this->cProducerConfig,
+                                                                            (long)batchingMaxPublishDelayMs);
+        }
+    }
+
+    if (producerConfig.Has(CFG_BATCH_MAX_MSG) && producerConfig.Get(CFG_BATCH_MAX_MSG).IsNumber()) {
+        uint32_t batchingMaxMessages = producerConfig.Get(CFG_BATCH_MAX_MSG).ToNumber().Uint32Value();
+        if (batchingMaxMessages > 0) {
+            pulsar_producer_configuration_set_batching_max_messages(this->cProducerConfig,
+                                                                    batchingMaxMessages);
+        }
+    }
+
+    if (producerConfig.Has(CFG_PROPS) && producerConfig.Get(CFG_PROPS).IsObject()) {
+        Napi::Object propObj = producerConfig.Get(CFG_PROPS).ToObject();
+        Napi::Array arr = propObj.GetPropertyNames();
+        int size = arr.Length();
+        for (int i = 0; i < size; i++) {
+            Napi::String key = arr.Get(i).ToString();
+            Napi::String value = propObj.Get(key).ToString();
+            pulsar_producer_configuration_set_property(this->cProducerConfig, key.Utf8Value().c_str(),
+                                                       value.Utf8Value().c_str());
+        }
+    }
+}
+
+ProducerConfig::~ProducerConfig() { pulsar_producer_configuration_free(this->cProducerConfig); }
+
+pulsar_producer_configuration_t* ProducerConfig::GetCProducerConfig() { return this->cProducerConfig; }
+
+std::string ProducerConfig::GetTopic() { return this->topic; }
diff --git a/src/ProducerConfig.h b/src/ProducerConfig.h
new file mode 100644
index 0000000..e2ecad0
--- /dev/null
+++ b/src/ProducerConfig.h
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef PRODUCER_CONFIG_H
+#define PRODUCER_CONFIG_H
+
+#include <napi.h>
+#include <pulsar/c/producer_configuration.h>
+
+class ProducerConfig {
+   public:
+    ProducerConfig(const Napi::Object &producerConfig);
+    ~ProducerConfig();
+    pulsar_producer_configuration_t *GetCProducerConfig();
+    std::string GetTopic();
+
+   private:
+    pulsar_producer_configuration_t *cProducerConfig;
+    std::string topic;
+};
+
+#endif
diff --git a/src/addon.cc b/src/addon.cc
new file mode 100644
index 0000000..abcbb24
--- /dev/null
+++ b/src/addon.cc
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "Client.h"
+#include "Consumer.h"
+#include "Message.h"
+#include "MessageId.h"
+#include "Producer.h"
+#include <napi.h>
+
+Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
+    Message::Init(env, exports);
+    MessageId::Init(env, exports);
+    Producer::Init(env, exports);
+    Consumer::Init(env, exports);
+    return Client::Init(env, exports);
+}
+
+NODE_API_MODULE(NODE_GYP_MODULE_NAME, InitAll)


[pulsar-client-node] 04/36: make config inline

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 05c9e48f397522395e0411d3c28aad354ac05e9a
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Tue Mar 12 10:55:19 2019 +0900

    make config inline
---
 examples/consumer.js | 10 ++++------
 examples/producer.js | 10 ++++------
 2 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/examples/consumer.js b/examples/consumer.js
index 7092c6b..c754ac3 100644
--- a/examples/consumer.js
+++ b/examples/consumer.js
@@ -21,19 +21,17 @@ const Pulsar = require('../index.js');
 
 (async () => {
   // Create a client
-  const clientConfig = {
+  const client = new Pulsar.Client({
     serviceUrl: 'pulsar://localhost:6650',
     operationTimeoutSeconds: 30,
-  };
-  const client = new Pulsar.Client(clientConfig);
+  });
 
   // Create a consumer
-  const consumerConfig = {
+  const consumer = await client.subscribe({
     topic: 'persistent://public/default/my-topic',
     subscription: 'sub1',
     ackTimeoutMs: 10000,
-  };
-  const consumer = await client.subscribe(consumerConfig);
+  });
 
   // Receive messages
   for (let i = 0; i < 10; i += 1) {
diff --git a/examples/producer.js b/examples/producer.js
index b1b6856..8730719 100644
--- a/examples/producer.js
+++ b/examples/producer.js
@@ -21,19 +21,17 @@ const Pulsar = require('../index.js');
 
 (async () => {
   // Create a client
-  const clientConfig = {
+  const client = new Pulsar.Client({
     serviceUrl: 'pulsar://localhost:6650',
     operationTimeoutSeconds: 30,
-  };
-  const client = new Pulsar.Client(clientConfig);
+  });
 
   // Create a producer
-  const producerConfig = {
+  const producer = await client.createProducer({
     topic: 'persistent://public/default/my-topic',
     sendTimeoutMs: 30000,
     batchingEnabled: true,
-  };
-  const producer = await client.createProducer(producerConfig);
+  });
 
   // Send messages
   const results = [];


[pulsar-client-node] 30/36: name topics test title

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 1b62170927a199435d7119814275ecdc2b33aed0
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Thu Jun 13 11:17:50 2019 +0900

    name topics test title
---
 tests/end_to_end.test.js | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/tests/end_to_end.test.js b/tests/end_to_end.test.js
index 5387cdd..b713d99 100644
--- a/tests/end_to_end.test.js
+++ b/tests/end_to_end.test.js
@@ -29,15 +29,16 @@ const Pulsar = require('../index.js');
         operationTimeoutSeconds: 30,
       });
 
+      const topic = 'persistent://public/default/produce-consume';
       const producer = await client.createProducer({
-        topic: 'persistent://public/default/test-end-to-end',
+        topic,
         sendTimeoutMs: 30000,
         batchingEnabled: true,
       });
       expect(producer).not.toBeNull();
 
       const consumer = await client.subscribe({
-        topic: 'persistent://public/default/test-end-to-end',
+        topic,
         subscription: 'sub1',
         ackTimeoutMs: 10000,
       });
@@ -73,15 +74,16 @@ const Pulsar = require('../index.js');
         operationTimeoutSeconds: 30,
       });
 
+      const topic = 'persistent://public/default/acknowledgeCumulative';
       const producer = await client.createProducer({
-        topic: 'persistent://public/default/acknowledgeCumulative',
+        topic,
         sendTimeoutMs: 30000,
         batchingEnabled: true,
       });
       expect(producer).not.toBeNull();
 
       const consumer = await client.subscribe({
-        topic: 'persistent://public/default/acknowledgeCumulative',
+        topic,
         subscription: 'sub1',
         ackTimeoutMs: 10000,
       });
@@ -118,15 +120,16 @@ const Pulsar = require('../index.js');
       });
       expect(client).not.toBeNull();
 
+      const topic = 'persistent://public/default/produce-read';
       const producer = await client.createProducer({
-        topic: 'persistent://public/default/test-end-to-end',
+        topic,
         sendTimeoutMs: 30000,
         batchingEnabled: true,
       });
       expect(producer).not.toBeNull();
 
       const reader = await client.createReader({
-        topic: 'persistent://public/default/test-end-to-end',
+        topic,
         startMessageId: Pulsar.MessageId.latest(),
       });
       expect(reader).not.toBeNull();


[pulsar-client-node] 08/36: Support Producer.flush()

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit e0a82d9a0e894b8b699492be7f185a3c4d025c67
Author: Masahiro Sakamoto <ma...@yahoo-corp.jp>
AuthorDate: Thu Mar 14 16:16:17 2019 +0900

    Support Producer.flush()
---
 examples/producer.js  |  7 +++----
 package-lock.json     |  2 +-
 perf/perf_producer.js |  7 +++----
 src/Producer.cc       | 39 +++++++++++++++++++++++++++++++++++++--
 src/Producer.h        |  1 +
 5 files changed, 45 insertions(+), 11 deletions(-)

diff --git a/examples/producer.js b/examples/producer.js
index 8730719..2dda9cc 100644
--- a/examples/producer.js
+++ b/examples/producer.js
@@ -34,15 +34,14 @@ const Pulsar = require('../index.js');
   });
 
   // Send messages
-  const results = [];
   for (let i = 0; i < 10; i += 1) {
     const msg = `my-message-${i}`;
-    results.push(producer.send({
+    producer.send({
       data: Buffer.from(msg),
-    }));
+    });
     console.log(`Sent message: ${msg}`);
   }
-  await Promise.all(results);
+  await producer.flush();
 
   await producer.close();
   await client.close();
diff --git a/package-lock.json b/package-lock.json
index 25d495e..0adfd94 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "pulsar-client",
-  "version": "0.0.1",
+  "version": "2.4.0-SNAPSHOT",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/perf/perf_producer.js b/perf/perf_producer.js
index 19f251c..fbe12f1 100644
--- a/perf/perf_producer.js
+++ b/perf/perf_producer.js
@@ -85,17 +85,16 @@ const Pulsar = require('../index.js');
     // measure
     await delay(1000);
     const startMeasureTimeMilliSeconds = performance.now();
-    const results = [];
     for (let mi = 0; mi < numOfMessages; mi += 1) {
       const startSendTimeMilliSeconds = performance.now();
-      results.push(producer.send({
+      producer.send({
         data: message,
       }).then(() => {
         // add latency
         histogram.recordValue((performance.now() - startSendTimeMilliSeconds));
-      }));
+      });
     }
-    await Promise.all(results);
+    await producer.flush();
     const endMeasureTimeMilliSeconds = performance.now();
 
     // result
diff --git a/src/Producer.cc b/src/Producer.cc
index a19d828..041695e 100644
--- a/src/Producer.cc
+++ b/src/Producer.cc
@@ -27,8 +27,10 @@ Napi::FunctionReference Producer::constructor;
 void Producer::Init(Napi::Env env, Napi::Object exports) {
   Napi::HandleScope scope(env);
 
-  Napi::Function func = DefineClass(
-      env, "Producer", {InstanceMethod("send", &Producer::Send), InstanceMethod("close", &Producer::Close)});
+  Napi::Function func =
+      DefineClass(env, "Producer",
+                  {InstanceMethod("send", &Producer::Send), InstanceMethod("flush", &Producer::Flush),
+                   InstanceMethod("close", &Producer::Close)});
 
   constructor = Napi::Persistent(func);
   constructor.SuppressDestruct();
@@ -119,6 +121,39 @@ Napi::Value Producer::Send(const Napi::CallbackInfo &info) {
   return deferred.Promise();
 }
 
+class ProducerFlushWorker : public Napi::AsyncWorker {
+ public:
+  ProducerFlushWorker(const Napi::Promise::Deferred &deferred, pulsar_producer_t *cProducer)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cProducer(cProducer) {}
+
+  ~ProducerFlushWorker() {}
+
+  void Execute() {
+    pulsar_result result = pulsar_producer_flush(this->cProducer);
+    if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+  }
+
+  void OnOK() { this->deferred.Resolve(Env().Null()); }
+
+  void OnError(const Napi::Error &e) {
+    this->deferred.Reject(
+        Napi::Error::New(Env(), std::string("Failed to flush producer: ") + e.Message()).Value());
+  }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_producer_t *cProducer;
+};
+
+Napi::Value Producer::Flush(const Napi::CallbackInfo &info) {
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  ProducerFlushWorker *wk = new ProducerFlushWorker(deferred, this->cProducer);
+  wk->Queue();
+  return deferred.Promise();
+}
+
 class ProducerCloseWorker : public Napi::AsyncWorker {
  public:
   ProducerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_producer_t *cProducer)
diff --git a/src/Producer.h b/src/Producer.h
index 5d31cfc..ea9b112 100644
--- a/src/Producer.h
+++ b/src/Producer.h
@@ -36,6 +36,7 @@ class Producer : public Napi::ObjectWrap<Producer> {
  private:
   pulsar_producer_t *cProducer;
   Napi::Value Send(const Napi::CallbackInfo &info);
+  Napi::Value Flush(const Napi::CallbackInfo &info);
   Napi::Value Close(const Napi::CallbackInfo &info);
 };
 


[pulsar-client-node] 07/36: Merge pull request #1 from k2la/provide_nodejs_client

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 2f977b959c0b5013987a77b9a4ce6d3d79c2c14c
Merge: 060a662 987578e
Author: Matteo Merli <mm...@apache.org>
AuthorDate: Tue Mar 12 09:34:05 2019 -0700

    Merge pull request #1 from k2la/provide_nodejs_client
    
    Provide Node Client Library

 .clang-format         |   25 +
 .eslintrc.json        |    9 +
 .gitignore            |    3 +
 Gruntfile.js          |   31 +
 README.md             |   56 +-
 binding.gyp           |   42 +
 examples/consumer.js  |   45 +
 examples/producer.js  |   49 +
 index.js              |   28 +
 package-lock.json     | 2762 +++++++++++++++++++++++++++++++++++++++++++++++++
 package.json          |   46 +
 perf/perf_consumer.js |  103 ++
 perf/perf_producer.js |  119 +++
 src/Client.cc         |  170 +++
 src/Client.h          |   41 +
 src/Consumer.cc       |  183 ++++
 src/Consumer.h        |   44 +
 src/ConsumerConfig.cc |  105 ++
 src/ConsumerConfig.h  |   44 +
 src/Message.cc        |  197 ++++
 src/Message.h         |   64 ++
 src/MessageId.cc      |   87 ++
 src/MessageId.h       |   46 +
 src/Producer.cc       |  151 +++
 src/Producer.h        |   42 +
 src/ProducerConfig.cc |  158 +++
 src/ProducerConfig.h  |   38 +
 src/addon.cc          |   35 +
 28 files changed, 4722 insertions(+), 1 deletion(-)


[pulsar-client-node] 34/36: Merge pull request #34 from shustsud/Add_subscription_type_to_examples

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 3fa417f8da7cf28fa94307b15a266163d815ce13
Merge: 1d03ff6 d95f5d1
Author: nkurihar <nk...@apache.org>
AuthorDate: Thu Jun 13 13:10:20 2019 +0900

    Merge pull request #34 from shustsud/Add_subscription_type_to_examples
    
    Add subscription type to examples.

 examples/consumer.js          | 1 +
 examples/consumer_tls_auth.js | 1 +
 2 files changed, 2 insertions(+)


[pulsar-client-node] 33/36: Merge pull request #33 from k2la/modify_test_topic

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 1d03ff69c57f6e2d105c606e56eaa297817f0975
Merge: 8b20d3d 1b62170
Author: nkurihar <nk...@apache.org>
AuthorDate: Thu Jun 13 13:09:57 2019 +0900

    Merge pull request #33 from k2la/modify_test_topic
    
    Fix TopicName in e2e Tests.

 tests/end_to_end.test.js | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)


[pulsar-client-node] 19/36: Merge pull request #28 from hrsakai/vulnerability

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit f2200131979f7dcadb1098392a10fc1361546108
Merge: 4398c89 869a5e9
Author: nkurihar <nk...@apache.org>
AuthorDate: Tue Apr 23 12:59:15 2019 +0900

    Merge pull request #28 from hrsakai/vulnerability
    
    Upgrade js-yaml to fix security vulnerability

 package-lock.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)


[pulsar-client-node] 32/36: move libraries in perf to devDependencies

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 0f9254eab006ebd46e3405564d20301067665d35
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Thu Jun 13 12:42:25 2019 +0900

    move libraries in perf to devDependencies
---
 .eslintignore |  1 +
 package.json  | 10 +++++-----
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..bd14107
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+perf
diff --git a/package.json b/package.json
index f974454..9128e58 100644
--- a/package.json
+++ b/package.json
@@ -29,20 +29,20 @@
   },
   "devDependencies": {
     "clang-format": "^1.2.4",
+    "commander": "^2.20.0",
+    "delay": "^4.3.0",
     "eslint": "^5.12.0",
     "eslint-config-airbnb-base": "^13.1.0",
     "eslint-plugin-import": "^2.14.0",
     "grunt": "^1.0.4",
     "grunt-license-report": "^0.0.8",
+    "hdr-histogram-js": "^1.1.4",
     "jest": "^24.7.1",
+    "lodash": "^4.17.11",
     "node-addon-api": "^1.6.2",
     "node-gyp": "^3.8.0"
   },
   "dependencies": {
-    "bindings": "^1.3.1",
-    "commander": "^2.19.0",
-    "delay": "^4.1.0",
-    "hdr-histogram-js": "^1.1.4",
-    "lodash": "^4.17.11"
+    "bindings": "^1.3.1"
   }
 }


[pulsar-client-node] 36/36: Merge pull request #35 from k2la/lighten_client

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 006871d205a4a3f8919a8537c4d1a4b10c45849b
Merge: 3fa417f 4ed9ce0
Author: nkurihar <nk...@apache.org>
AuthorDate: Thu Jun 13 15:28:33 2019 +0900

    Merge pull request #35 from k2la/lighten_client
    
    Move Libraries in Perf to devDependencies

 .eslintignore     |  1 +
 package-lock.json | 24 +++++++++++++++---------
 package.json      | 10 +++++-----
 3 files changed, 21 insertions(+), 14 deletions(-)


[pulsar-client-node] 23/36: Merge pull request #29 from hrsakai/ack_cumulative

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit d07163e673a9a5bc2b1d037ea0482d81634b4629
Merge: b6ce761 a945e49
Author: massakam <ma...@yahoo-corp.jp>
AuthorDate: Mon Jun 10 19:04:49 2019 +0900

    Merge pull request #29 from hrsakai/ack_cumulative
    
     Add acknowledgeCumulative / receiveWithTimeout methods

 pulsar-test-service-start.sh |  2 +-
 run-unit-tests.sh            |  2 +-
 src/Consumer.cc              | 52 +++++++++++++++++++++++++++++++++++---------
 src/Consumer.h               |  3 +++
 tests/end_to_end.test.js     | 39 +++++++++++++++++++++++++++++++++
 5 files changed, 86 insertions(+), 12 deletions(-)


[pulsar-client-node] 18/36: Add file which defines pulsar version

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit ed17fee49e4665f6009ca199be5bde2c3df8e866
Author: hrsakai <hs...@yahoo-corp.jp>
AuthorDate: Tue Apr 23 12:09:29 2019 +0900

    Add file which defines pulsar version
---
 pulsar-test-service-start.sh | 2 +-
 pulsar-version.txt           | 1 +
 run-unit-tests.sh            | 5 ++++-
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/pulsar-test-service-start.sh b/pulsar-test-service-start.sh
index af9ad03..78e715e 100755
--- a/pulsar-test-service-start.sh
+++ b/pulsar-test-service-start.sh
@@ -23,7 +23,7 @@ set -e
 ROOT_DIR=$(git rev-parse --show-toplevel)
 cd $ROOT_DIR
 
-VERSION="${VERSION:-2.3.1}"
+VERSION="${VERSION:-`cat ./pulsar-version.txt`}"
 PULSAR_DIR="${PULSAR_DIR:-/tmp/pulsar-test-dist}"
 PKG=apache-pulsar-${VERSION}-bin.tar.gz
 
diff --git a/pulsar-version.txt b/pulsar-version.txt
new file mode 100755
index 0000000..2bf1c1c
--- /dev/null
+++ b/pulsar-version.txt
@@ -0,0 +1 @@
+2.3.1
diff --git a/run-unit-tests.sh b/run-unit-tests.sh
index c8358a8..1acce0a 100755
--- a/run-unit-tests.sh
+++ b/run-unit-tests.sh
@@ -20,8 +20,11 @@
 
 set -e
 
+ROOT_DIR=$(git rev-parse --show-toplevel)
+cd $ROOT_DIR
+
 # install pulsar cpp client pkg
-VERSION="${VERSION:-2.3.1}"
+VERSION="${VERSION:-`cat ./pulsar-version.txt`}"
 PULSAR_PKG_DIR="/tmp/pulsar-test-pkg"
 rm -rf $PULSAR_PKG_DIR
 for pkg in apache-pulsar-client-dev.deb apache-pulsar-client.deb;do


[pulsar-client-node] 09/36: Merge pull request #23 from massakam/producer-flush

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 87e76666d3a144a4589f0f3084605058362849dc
Merge: 2f977b9 e0a82d9
Author: Matteo Merli <mm...@apache.org>
AuthorDate: Thu Mar 14 13:20:28 2019 -0700

    Merge pull request #23 from massakam/producer-flush
    
    [Issue 2] Support Producer.flush()

 examples/producer.js  |  7 +++----
 package-lock.json     |  2 +-
 perf/perf_producer.js |  7 +++----
 src/Producer.cc       | 39 +++++++++++++++++++++++++++++++++++++--
 src/Producer.h        |  1 +
 5 files changed, 45 insertions(+), 11 deletions(-)


[pulsar-client-node] 29/36: Merge pull request #31 from k2la/reader

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 8b20d3d42a79b2267d89a2209fbf0f11ace5bf45
Merge: 3561f2b ab3db36
Author: massakam <ma...@yahoo-corp.jp>
AuthorDate: Wed Jun 12 11:43:43 2019 +0900

    Merge pull request #31 from k2la/reader
    
    Implement Reader

 binding.gyp                        |   2 +
 src/Client.h => examples/reader.js |  38 ++++----
 src/Client.cc                      |  14 ++-
 src/Client.h                       |   1 +
 src/Reader.cc                      | 188 +++++++++++++++++++++++++++++++++++++
 src/{Client.h => Reader.h}         |  22 +++--
 src/ReaderConfig.cc                |  70 ++++++++++++++
 src/{Client.h => ReaderConfig.h}   |  27 +++---
 src/addon.cc                       |   2 +
 tests/end_to_end.test.js           |  61 +++++++++++-
 tests/reader.test.js               |  72 ++++++++++++++
 11 files changed, 448 insertions(+), 49 deletions(-)



[pulsar-client-node] 16/36: Add scripts for tests in docker container

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 37b447b2eb0531fa2e592ff02ded8821ae970367
Author: hrsakai <hs...@yahoo-corp.jp>
AuthorDate: Mon Apr 22 13:57:38 2019 +0900

    Add scripts for tests in docker container
---
 docker-tests.sh              |  37 ++++++
 pulsar-test-service-start.sh |  45 ++++++++
 pulsar-test-service-stop.sh  |  26 +++++
 run-unit-tests.sh            |  37 ++++++
 tests/conf/standalone.conf   | 265 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 410 insertions(+)

diff --git a/docker-tests.sh b/docker-tests.sh
new file mode 100755
index 0000000..fab1042
--- /dev/null
+++ b/docker-tests.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+ROOT_DIR=$(git rev-parse --show-toplevel)
+cd $ROOT_DIR
+
+BUILD_IMAGE_NAME="${BUILD_IMAGE_NAME:-apachepulsar/pulsar-build}"
+BUILD_IMAGE_VERSION="${BUILD_IMAGE_VERSION:-ubuntu-16.04}"
+
+IMAGE="$BUILD_IMAGE_NAME:$BUILD_IMAGE_VERSION"
+
+echo "---- Testing Pulsar node client using image $IMAGE"
+
+docker pull $IMAGE
+
+DOCKER_CMD="docker run -i -v $ROOT_DIR:/pulsar-client-node $IMAGE"
+
+# Start Pulsar standalone instance
+# and execute the tests
+$DOCKER_CMD bash -c "cd /pulsar-client-node && ./run-unit-tests.sh"
diff --git a/pulsar-test-service-start.sh b/pulsar-test-service-start.sh
new file mode 100755
index 0000000..af9ad03
--- /dev/null
+++ b/pulsar-test-service-start.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -e
+
+ROOT_DIR=$(git rev-parse --show-toplevel)
+cd $ROOT_DIR
+
+VERSION="${VERSION:-2.3.1}"
+PULSAR_DIR="${PULSAR_DIR:-/tmp/pulsar-test-dist}"
+PKG=apache-pulsar-${VERSION}-bin.tar.gz
+
+rm -rf $PULSAR_DIR
+curl -L --create-dir "https://www.apache.org/dyn/mirrors/mirrors.cgi?action=download&filename=pulsar/pulsar-${VERSION}/${PKG}" -o $PULSAR_DIR/$PKG
+tar xfz $PULSAR_DIR/$PKG -C $PULSAR_DIR --strip-components 1
+
+DATA_DIR=/tmp/pulsar-test-data
+rm -rf $DATA_DIR
+mkdir -p $DATA_DIR
+
+export PULSAR_STANDALONE_CONF=$ROOT_DIR/tests/conf/standalone.conf
+$PULSAR_DIR/bin/pulsar-daemon start standalone \
+        --no-functions-worker --no-stream-storage \
+        --zookeeper-dir $DATA_DIR/zookeeper \
+        --bookkeeper-dir $DATA_DIR/bookkeeper
+
+echo "-- Wait for Pulsar service to be ready"
+until curl http://localhost:8080/metrics > /dev/null 2>&1 ; do sleep 1; done
diff --git a/pulsar-test-service-stop.sh b/pulsar-test-service-stop.sh
new file mode 100755
index 0000000..f162426
--- /dev/null
+++ b/pulsar-test-service-stop.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -e
+
+PULSAR_DIR="${PULSAR_DIR:-/tmp/pulsar-test-dist}"
+cd $PULSAR_DIR
+
+bin/pulsar-daemon stop standalone
diff --git a/run-unit-tests.sh b/run-unit-tests.sh
new file mode 100755
index 0000000..c8358a8
--- /dev/null
+++ b/run-unit-tests.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -e
+
+# install pulsar cpp client pkg
+VERSION="${VERSION:-2.3.1}"
+PULSAR_PKG_DIR="/tmp/pulsar-test-pkg"
+rm -rf $PULSAR_PKG_DIR
+for pkg in apache-pulsar-client-dev.deb apache-pulsar-client.deb;do
+  curl -L --create-dir "https://www.apache.org/dyn/mirrors/mirrors.cgi?action=download&filename=pulsar/pulsar-${VERSION}/DEB/${pkg}" -o $PULSAR_PKG_DIR/$pkg
+done;
+apt install $PULSAR_PKG_DIR/apache-pulsar-client*.deb
+
+./pulsar-test-service-start.sh
+npm install && npm run build && npm run test
+RES=$?
+./pulsar-test-service-stop.sh
+
+exit $RES
diff --git a/tests/conf/standalone.conf b/tests/conf/standalone.conf
new file mode 100755
index 0000000..18d4c42
--- /dev/null
+++ b/tests/conf/standalone.conf
@@ -0,0 +1,265 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+### --- General broker settings --- ###
+
+# Zookeeper quorum connection string
+zookeeperServers=
+
+# Global Zookeeper quorum connection string
+globalZookeeperServers=
+
+brokerServicePort=6650
+
+# Port to use to server HTTP request
+webServicePort=8080
+
+# Hostname or IP address the service binds on, default is 0.0.0.0.
+bindAddress=0.0.0.0
+
+# Hostname or IP address the service advertises to the outside world. If not set, the value of InetAddress.getLocalHost().getHostName() is used.
+advertisedAddress=localhost
+
+# Name of the cluster to which this broker belongs to
+clusterName=standalone
+
+# Zookeeper session timeout in milliseconds
+zooKeeperSessionTimeoutMillis=30000
+
+# Time to wait for broker graceful shutdown. After this time elapses, the process will be killed
+brokerShutdownTimeoutMs=3000
+
+# Enable backlog quota check. Enforces action on topic when the quota is reached
+backlogQuotaCheckEnabled=true
+
+# How often to check for topics that have reached the quota
+backlogQuotaCheckIntervalInSeconds=60
+
+# Default per-topic backlog quota limit
+backlogQuotaDefaultLimitGB=10
+
+# Enable the deletion of inactive topics
+brokerDeleteInactiveTopicsEnabled=true
+
+# How often to check for inactive topics
+brokerDeleteInactiveTopicsFrequencySeconds=60
+
+# How frequently to proactively check and purge expired messages 
+messageExpiryCheckIntervalInMinutes=5
+
+# Enable check for minimum allowed client library version
+clientLibraryVersionCheckEnabled=false
+
+# Allow client libraries with no version information 
+clientLibraryVersionCheckAllowUnversioned=true
+
+# Path for the file used to determine the rotation status for the broker when responding
+# to service discovery health checks
+statusFilePath=/usr/local/apache/htdocs
+
+# Max number of unacknowledged messages allowed to receive messages by a consumer on a shared subscription. Broker will stop sending
+# messages to consumer once, this limit reaches until consumer starts acknowledging messages back
+# Using a value of 0, is disabling unackeMessage limit check and consumer can receive messages without any restriction
+maxUnackedMessagesPerConsumer=50000
+
+### --- Authentication --- ###
+
+# Enable authentication
+authenticationEnabled=false
+
+# Autentication provider name list, which is comma separated list of class names
+authenticationProviders=false
+
+# Enforce authorization
+authorizationEnabled=false
+
+# Role names that are treated as "super-user", meaning they will be able to do all admin
+# operations and publish/consume from all topics
+superUserRoles=
+
+# Authentication settings of the broker itself. Used when the broker connects to other brokers,
+# either in same or other clusters
+brokerClientAuthenticationPlugin=
+brokerClientAuthenticationParameters=
+
+
+### --- BookKeeper Client --- ###
+
+# Authentication plugin to use when connecting to bookies
+bookkeeperClientAuthenticationPlugin=
+
+# BookKeeper auth plugin implementatation specifics parameters name and values
+bookkeeperClientAuthenticationParametersName=
+bookkeeperClientAuthenticationParameters=
+
+# Timeout for BK add / read operations 
+bookkeeperClientTimeoutInSeconds=30
+
+# Speculative reads are initiated if a read request doesn't complete within a certain time
+# Using a value of 0, is disabling the speculative reads
+bookkeeperClientSpeculativeReadTimeoutInMillis=0
+
+# Enable bookies health check. Bookies that have more than the configured number of failure within
+# the interval will be quarantined for some time. During this period, new ledgers won't be created
+# on these bookies
+bookkeeperClientHealthCheckEnabled=true
+bookkeeperClientHealthCheckIntervalSeconds=60
+bookkeeperClientHealthCheckErrorThresholdPerInterval=5
+bookkeeperClientHealthCheckQuarantineTimeInSeconds=1800
+
+# Enable rack-aware bookie selection policy. BK will chose bookies from different racks when
+# forming a new bookie ensemble 
+bookkeeperClientRackawarePolicyEnabled=true
+
+# Enable region-aware bookie selection policy. BK will chose bookies from
+# different regions and racks when forming a new bookie ensemble
+# If enabled, the value of bookkeeperClientRackawarePolicyEnabled is ignored
+bookkeeperClientRegionawarePolicyEnabled=false
+
+# Enable/disable reordering read sequence on reading entries.
+bookkeeperClientReorderReadSequenceEnabled=false
+
+# Enable bookie isolation by specifying a list of bookie groups to choose from. Any bookie 
+# outside the specified groups will not be used by the broker 
+bookkeeperClientIsolationGroups=
+
+### --- Managed Ledger --- ###
+
+# Number of bookies to use when creating a ledger
+managedLedgerDefaultEnsembleSize=1
+
+# Number of copies to store for each message
+managedLedgerDefaultWriteQuorum=1
+
+# Number of guaranteed copies (acks to wait before write is complete)
+managedLedgerDefaultAckQuorum=1
+
+# Amount of memory to use for caching data payload in managed ledger. This memory
+# is allocated from JVM direct memory and it's shared across all the topics
+# running  in the same broker 
+managedLedgerCacheSizeMB=1024
+
+# Threshold to which bring down the cache level when eviction is triggered
+managedLedgerCacheEvictionWatermark=0.9
+
+# Rate limit the amount of writes generated by consumer acking the messages
+managedLedgerDefaultMarkDeleteRateLimit=0.1
+
+# Max number of entries to append to a ledger before triggering a rollover
+# A ledger rollover is triggered on these conditions
+#  * Either the max rollover time has been reached
+#  * or max entries have been written to the ledged and at least min-time 
+#    has passed
+managedLedgerMaxEntriesPerLedger=50000
+
+# Minimum time between ledger rollover for a topic
+managedLedgerMinLedgerRolloverTimeMinutes=10
+
+# Maximum time before forcing a ledger rollover for a topic
+managedLedgerMaxLedgerRolloverTimeMinutes=240
+
+# Max number of entries to append to a cursor ledger
+managedLedgerCursorMaxEntriesPerLedger=50000
+
+# Max time before triggering a rollover on a cursor ledger
+managedLedgerCursorRolloverTimeInSeconds=14400
+
+
+
+### --- Load balancer --- ### 
+
+# Enable load balancer
+loadBalancerEnabled=false
+
+# Strategy to assign a new bundle
+loadBalancerPlacementStrategy=weightedRandomSelection
+
+# Percentage of change to trigger load report update
+loadBalancerReportUpdateThresholdPercentage=10
+
+# maximum interval to update load report
+loadBalancerReportUpdateMaxIntervalMinutes=15
+
+# Frequency of report to collect
+loadBalancerHostUsageCheckIntervalMinutes=1
+
+# Load shedding interval. Broker periodically checks whether some traffic should be offload from
+# some over-loaded broker to other under-loaded brokers   
+loadBalancerSheddingIntervalMinutes=30
+
+# Prevent the same topics to be shed and moved to other broker more that once within this timeframe 
+loadBalancerSheddingGracePeriodMinutes=30
+
+# Usage threshold to determine a broker as under-loaded 
+loadBalancerBrokerUnderloadedThresholdPercentage=1
+
+# Usage threshold to determine a broker as over-loaded
+loadBalancerBrokerOverloadedThresholdPercentage=85
+
+# Interval to update namespace bundle resource quotat
+loadBalancerResourceQuotaUpdateIntervalMinutes=15
+
+# Usage threshold to determine a broker is having just right level of load
+loadBalancerBrokerComfortLoadLevelPercentage=65
+
+# enable/disable namespace bundle auto split
+loadBalancerAutoBundleSplitEnabled=false
+
+# interval to detect & split hot namespace bundle
+loadBalancerNamespaceBundleSplitIntervalMinutes=15
+
+# maximum topics in a bundle, otherwise bundle split will be triggered
+loadBalancerNamespaceBundleMaxTopics=1000
+
+# maximum sessions (producers + consumers) in a bundle, otherwise bundle split will be triggered
+loadBalancerNamespaceBundleMaxSessions=1000
+
+# maximum msgRate (in + out) in a bundle, otherwise bundle split will be triggered
+loadBalancerNamespaceBundleMaxMsgRate=1000
+
+# maximum bandwidth (in + out) in a bundle, otherwise bundle split will be triggered
+loadBalancerNamespaceBundleMaxBandwidthMbytes=100
+
+# maximum number of bundles in a namespace
+loadBalancerNamespaceMaximumBundles=128
+
+### --- Replication --- ###
+
+# Enable replication metrics
+replicationMetricsEnabled=true
+
+# Max number of connections to open for each broker in a remote cluster
+# More connections host-to-host lead to better throughput over high-latency
+# links. 
+replicationConnectionsPerBroker=16
+
+# Replicator producer queue size
+replicationProducerQueueSize=1000
+
+# Default message retention time
+defaultRetentionTimeInMinutes=0
+
+# Default retention size
+defaultRetentionSizeInMB=0
+
+# How often to check whether the connections are still alive
+keepAliveIntervalSeconds=30
+
+# How often broker checks for inactive topics to be deleted (topics with no subscriptions and no one connected)
+brokerServicePurgeInactiveFrequencyInSeconds=60


[pulsar-client-node] 05/36: add compatibility and change version

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit d4513f7b3e03df6cd185f3cec3f81a74a5d4fe25
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Tue Mar 12 12:45:36 2019 +0900

    add compatibility and change version
---
 README.md    | 4 ++++
 package.json | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 90d856f..0418c32 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,10 @@
 
 The Pulsar Node.js client can be used to create Pulsar producers and consumers in Node.js.
 
+## Compatibility
+
+This Node.js client is developed and tested using Apache Pulsar 2.3.0
+
 ## Requirements
 
 Pulsar Node.js client library is based on the C++ client library. Follow the instructions for
diff --git a/package.json b/package.json
index 33b1321..cda8e3c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pulsar-client",
-  "version": "0.0.1",
+  "version": "2.3.0",
   "description": "Pulsar Node.js client",
   "main": "index.js",
   "directories": {


[pulsar-client-node] 11/36: Merge pull request #24 from massakam/authentication

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit bfe9e38bb3a278e3dcb57ed7103810974c4fe1c3
Merge: 87e7666 0dccd84
Author: hrsakai <hs...@yahoo-corp.jp>
AuthorDate: Fri Mar 29 10:08:01 2019 +0900

    Merge pull request #24 from massakam/authentication
    
    [Issue 3] Support authentication

 binding.gyp                               |  3 +-
 index.js => examples/consumer_tls_auth.js | 36 +++++++++---
 index.js => examples/producer_tls_auth.js | 38 ++++++++++---
 index.js                                  |  6 ++
 src/Authentication.cc                     | 95 +++++++++++++++++++++++++++++++
 src/{addon.cc => Authentication.h}        | 29 +++++-----
 index.js => src/AuthenticationAthenz.js   | 13 +++--
 index.js => src/AuthenticationTls.js      | 12 ++--
 index.js => src/AuthenticationToken.js    | 12 ++--
 src/Client.cc                             | 11 ++++
 src/addon.cc                              |  6 +-
 11 files changed, 213 insertions(+), 48 deletions(-)


[pulsar-client-node] 31/36: Add subscription type to examples.

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit d95f5d155bab1671908e2925667d676c69934672
Author: shustsud <sh...@yahoo-corp.jp>
AuthorDate: Thu Jun 13 11:43:29 2019 +0900

    Add subscription type to examples.
    
    Add subscription type to examples.
---
 examples/consumer.js          | 1 +
 examples/consumer_tls_auth.js | 1 +
 2 files changed, 2 insertions(+)

diff --git a/examples/consumer.js b/examples/consumer.js
index c754ac3..f0b370d 100644
--- a/examples/consumer.js
+++ b/examples/consumer.js
@@ -30,6 +30,7 @@ const Pulsar = require('../index.js');
   const consumer = await client.subscribe({
     topic: 'persistent://public/default/my-topic',
     subscription: 'sub1',
+    subscriptionType: 'Shared',
     ackTimeoutMs: 10000,
   });
 
diff --git a/examples/consumer_tls_auth.js b/examples/consumer_tls_auth.js
index e71b251..7f846c5 100644
--- a/examples/consumer_tls_auth.js
+++ b/examples/consumer_tls_auth.js
@@ -36,6 +36,7 @@ const Pulsar = require('../index.js');
   const consumer = await client.subscribe({
     topic: 'persistent://public/default/my-topic',
     subscription: 'sub1',
+    subscriptionType: 'Shared',
   });
 
   // Receive messages


[pulsar-client-node] 13/36: Merge pull request #25 from massakam/update-js-yaml

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 3811a4f27b95fe4d407a0eca6a2666cf41e16544
Merge: bfe9e38 e938a7c
Author: nkurihar <nk...@apache.org>
AuthorDate: Thu Apr 4 13:48:59 2019 +0900

    Merge pull request #25 from massakam/update-js-yaml
    
    Update module with severity vulnerabilities

 package-lock.json | 44 ++++++++++++++++++--------------------------
 package.json      |  2 +-
 2 files changed, 19 insertions(+), 27 deletions(-)


[pulsar-client-node] 06/36: set new version

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 987578e694fceb6788f74224f90b6b8662a61f7a
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Tue Mar 12 14:44:20 2019 +0900

    set new version
---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index cda8e3c..2455ce6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pulsar-client",
-  "version": "2.3.0",
+  "version": "2.4.0-SNAPSHOT",
   "description": "Pulsar Node.js client",
   "main": "index.js",
   "directories": {


[pulsar-client-node] 28/36: delete vscode

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit ab3db36a08ad353e14bf13b54d101567d07df13a
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Wed Jun 12 11:41:45 2019 +0900

    delete vscode
---
 .vscode/ipch/cc6423c2fd8ec533/mmap_address.bin | Bin 8 -> 0 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/.vscode/ipch/cc6423c2fd8ec533/mmap_address.bin b/.vscode/ipch/cc6423c2fd8ec533/mmap_address.bin
deleted file mode 100644
index 71307ab..0000000
Binary files a/.vscode/ipch/cc6423c2fd8ec533/mmap_address.bin and /dev/null differ


[pulsar-client-node] 01/36: Initialize pulsar-client-node project

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 060a66246a38a3d9e367f41ba44c4f80db534838
Author: Sijie Guo <si...@apache.org>
AuthorDate: Fri Mar 1 21:47:17 2019 +0800

    Initialize pulsar-client-node project
---
 LICENSE   | 305 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 README.md |   1 +
 2 files changed, 306 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..356931c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,305 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+----------------------------------------------------------------------------------------------------
+
+pulsar-common/src/main/java/org/apache/pulsar/common/util/protobuf/ByteBufCoded{Input,Output}Stream.java
+
+Copyright 2014, Google Inc.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+    * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Code generated by the Protocol Buffer compiler is owned by the owner
+of the input file used when generating it.  This code is not
+standalone and requires a support library to be linked with it.  This
+support library is itself covered by the above license.
+
+----------------------------------------------------------------------------------------------------
+
+pulsar-client-cpp/lib/lz4/lz4.{h,c}
+
+LZ4 - Fast LZ compression algorithm
+Copyright (C) 2011-2015, Yann Collet.
+
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+You can contact the author at :
+- LZ4 source repository : https://github.com/Cyan4973/lz4
+- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+
+----------------------------------------------------------------------------------------------------
+
+pulsar-client-cpp/lib/checksum/crc32c_sw.cc
+
+/* crc32c.c -- compute CRC-32C using the Intel crc32 instruction
+ * Copyright (C) 2013 Mark Adler
+ * Version 1.1  1 Aug 2013  Mark Adler
+ */
+
+/*
+ This software is provided 'as-is', without any express or implied
+ warranty.  In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Mark Adler
+ madler@alumni.caltech.edu
+ */
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b560743
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# Pulsar Node Client


[pulsar-client-node] 15/36: Merge pull request #26 from k2la/add_test_code

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 4398c8956791ff07c13b94565e149a3abefb6035
Merge: 3811a4f 19aaaa3
Author: nkurihar <nk...@apache.org>
AuthorDate: Tue Apr 16 12:48:53 2019 +0900

    Merge pull request #26 from k2la/add_test_code
    
    Add Producer/Consumer/EndToEnd Tests

 .eslintrc.json           |    5 +
 package-lock.json        | 4243 +++++++++++++++++++++++++++++++++++++++++++++-
 package.json             |    6 +-
 tests/consumer.test.js   |   76 +
 tests/end_to_end.test.js |   68 +
 tests/producer.test.js   |   61 +
 6 files changed, 4363 insertions(+), 96 deletions(-)


[pulsar-client-node] 20/36: Merge pull request #27 from hrsakai/end-to-end-exec

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit b6ce7616995e6e300e53e3a441fd7bfa6d9d8db7
Merge: f220013 ed17fee
Author: nkurihar <nk...@apache.org>
AuthorDate: Tue Apr 23 12:59:35 2019 +0900

    Merge pull request #27 from hrsakai/end-to-end-exec
    
    Add scripts for tests in docker container

 docker-tests.sh              |  37 ++++++
 pulsar-test-service-start.sh |  45 ++++++++
 pulsar-test-service-stop.sh  |  26 +++++
 pulsar-version.txt           |   1 +
 run-unit-tests.sh            |  40 +++++++
 tests/conf/standalone.conf   | 265 +++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 414 insertions(+)


[pulsar-client-node] 14/36: add producer,consumer,endtoend test

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 19aaaa3089a772ce47ae18d98787d08ffb548c99
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Mon Apr 15 15:09:53 2019 +0900

    add producer,consumer,endtoend test
---
 .eslintrc.json           |    5 +
 package-lock.json        | 4243 +++++++++++++++++++++++++++++++++++++++++++++-
 package.json             |    6 +-
 tests/consumer.test.js   |   76 +
 tests/end_to_end.test.js |   68 +
 tests/producer.test.js   |   61 +
 6 files changed, 4363 insertions(+), 96 deletions(-)

diff --git a/.eslintrc.json b/.eslintrc.json
index 70e3c60..470a221 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -5,5 +5,10 @@
     "no-await-in-loop": "warn",
     "no-console": "off",
     "no-unused-vars": "warn"
+  },
+  "globals": {
+    "describe": true,
+    "test": true,
+    "expect": true
   }
 }
diff --git a/package-lock.json b/package-lock.json
index f150028..7e530be 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,103 @@
         "@babel/highlight": "^7.0.0"
       }
     },
+    "@babel/core": {
+      "version": "7.4.3",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.3.tgz",
+      "integrity": "sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "@babel/generator": "^7.4.0",
+        "@babel/helpers": "^7.4.3",
+        "@babel/parser": "^7.4.3",
+        "@babel/template": "^7.4.0",
+        "@babel/traverse": "^7.4.3",
+        "@babel/types": "^7.4.0",
+        "convert-source-map": "^1.1.0",
+        "debug": "^4.1.0",
+        "json5": "^2.1.0",
+        "lodash": "^4.17.11",
+        "resolve": "^1.3.2",
+        "semver": "^5.4.1",
+        "source-map": "^0.5.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "@babel/generator": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz",
+      "integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.4.0",
+        "jsesc": "^2.5.1",
+        "lodash": "^4.17.11",
+        "source-map": "^0.5.0",
+        "trim-right": "^1.0.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "@babel/helper-function-name": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz",
+      "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-get-function-arity": "^7.0.0",
+        "@babel/template": "^7.1.0",
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "@babel/helper-get-function-arity": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz",
+      "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "@babel/helper-plugin-utils": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz",
+      "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==",
+      "dev": true
+    },
+    "@babel/helper-split-export-declaration": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz",
+      "integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.4.0"
+      }
+    },
+    "@babel/helpers": {
+      "version": "7.4.3",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.3.tgz",
+      "integrity": "sha512-BMh7X0oZqb36CfyhvtbSmcWc3GXocfxv3yNsAEuM0l+fAqSO22rQrUpijr3oE/10jCTrB6/0b9kzmG4VetCj8Q==",
+      "dev": true,
+      "requires": {
+        "@babel/template": "^7.4.0",
+        "@babel/traverse": "^7.4.3",
+        "@babel/types": "^7.4.0"
+      }
+    },
     "@babel/highlight": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
@@ -24,6 +121,324 @@
         "js-tokens": "^4.0.0"
       }
     },
+    "@babel/parser": {
+      "version": "7.4.3",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.3.tgz",
+      "integrity": "sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ==",
+      "dev": true
+    },
+    "@babel/plugin-syntax-object-rest-spread": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz",
+      "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.0.0"
+      }
+    },
+    "@babel/template": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz",
+      "integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "@babel/parser": "^7.4.0",
+        "@babel/types": "^7.4.0"
+      }
+    },
+    "@babel/traverse": {
+      "version": "7.4.3",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.3.tgz",
+      "integrity": "sha512-HmA01qrtaCwwJWpSKpA948cBvU5BrmviAief/b3AVw936DtcdsTexlbyzNuDnthwhOQ37xshn7hvQaEQk7ISYQ==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "@babel/generator": "^7.4.0",
+        "@babel/helper-function-name": "^7.1.0",
+        "@babel/helper-split-export-declaration": "^7.4.0",
+        "@babel/parser": "^7.4.3",
+        "@babel/types": "^7.4.0",
+        "debug": "^4.1.0",
+        "globals": "^11.1.0",
+        "lodash": "^4.17.11"
+      }
+    },
+    "@babel/types": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz",
+      "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2",
+        "lodash": "^4.17.11",
+        "to-fast-properties": "^2.0.0"
+      }
+    },
+    "@cnakazawa/watch": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz",
+      "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==",
+      "dev": true,
+      "requires": {
+        "exec-sh": "^0.3.2",
+        "minimist": "^1.2.0"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
+    "@jest/console": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz",
+      "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==",
+      "dev": true,
+      "requires": {
+        "@jest/source-map": "^24.3.0",
+        "chalk": "^2.0.1",
+        "slash": "^2.0.0"
+      }
+    },
+    "@jest/core": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.7.1.tgz",
+      "integrity": "sha512-ivlZ8HX/FOASfHcb5DJpSPFps8ydfUYzLZfgFFqjkLijYysnIEOieg72YRhO4ZUB32xu40hsSMmaw+IGYeKONA==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^24.7.1",
+        "@jest/reporters": "^24.7.1",
+        "@jest/test-result": "^24.7.1",
+        "@jest/transform": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "ansi-escapes": "^3.0.0",
+        "chalk": "^2.0.1",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.1.15",
+        "jest-changed-files": "^24.7.0",
+        "jest-config": "^24.7.1",
+        "jest-haste-map": "^24.7.1",
+        "jest-message-util": "^24.7.1",
+        "jest-regex-util": "^24.3.0",
+        "jest-resolve-dependencies": "^24.7.1",
+        "jest-runner": "^24.7.1",
+        "jest-runtime": "^24.7.1",
+        "jest-snapshot": "^24.7.1",
+        "jest-util": "^24.7.1",
+        "jest-validate": "^24.7.0",
+        "jest-watcher": "^24.7.1",
+        "micromatch": "^3.1.10",
+        "p-each-series": "^1.0.0",
+        "pirates": "^4.0.1",
+        "realpath-native": "^1.1.0",
+        "rimraf": "^2.5.4",
+        "strip-ansi": "^5.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        }
+      }
+    },
+    "@jest/environment": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.7.1.tgz",
+      "integrity": "sha512-wmcTTYc4/KqA+U5h1zQd5FXXynfa7VGP2NfF+c6QeGJ7c+2nStgh65RQWNX62SC716dTtqheTRrZl0j+54oGHw==",
+      "dev": true,
+      "requires": {
+        "@jest/fake-timers": "^24.7.1",
+        "@jest/transform": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "jest-mock": "^24.7.0"
+      }
+    },
+    "@jest/fake-timers": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.7.1.tgz",
+      "integrity": "sha512-4vSQJDKfR2jScOe12L9282uiwuwQv9Lk7mgrCSZHA9evB9efB/qx8i0KJxsAKtp8fgJYBJdYY7ZU6u3F4/pyjA==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0",
+        "jest-message-util": "^24.7.1",
+        "jest-mock": "^24.7.0"
+      }
+    },
+    "@jest/reporters": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.7.1.tgz",
+      "integrity": "sha512-bO+WYNwHLNhrjB9EbPL4kX/mCCG4ZhhfWmO3m4FSpbgr7N83MFejayz30kKjgqr7smLyeaRFCBQMbXpUgnhAJw==",
+      "dev": true,
+      "requires": {
+        "@jest/environment": "^24.7.1",
+        "@jest/test-result": "^24.7.1",
+        "@jest/transform": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "chalk": "^2.0.1",
+        "exit": "^0.1.2",
+        "glob": "^7.1.2",
+        "istanbul-api": "^2.1.1",
+        "istanbul-lib-coverage": "^2.0.2",
+        "istanbul-lib-instrument": "^3.0.1",
+        "istanbul-lib-source-maps": "^3.0.1",
+        "jest-haste-map": "^24.7.1",
+        "jest-resolve": "^24.7.1",
+        "jest-runtime": "^24.7.1",
+        "jest-util": "^24.7.1",
+        "jest-worker": "^24.6.0",
+        "node-notifier": "^5.2.1",
+        "slash": "^2.0.0",
+        "source-map": "^0.6.0",
+        "string-length": "^2.0.0"
+      }
+    },
+    "@jest/source-map": {
+      "version": "24.3.0",
+      "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz",
+      "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0",
+        "graceful-fs": "^4.1.15",
+        "source-map": "^0.6.0"
+      }
+    },
+    "@jest/test-result": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.7.1.tgz",
+      "integrity": "sha512-3U7wITxstdEc2HMfBX7Yx3JZgiNBubwDqQMh+BXmZXHa3G13YWF3p6cK+5g0hGkN3iufg/vGPl3hLxQXD74Npg==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "@types/istanbul-lib-coverage": "^2.0.0"
+      }
+    },
+    "@jest/test-sequencer": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.7.1.tgz",
+      "integrity": "sha512-84HQkCpVZI/G1zq53gHJvSmhUer4aMYp9tTaffW28Ih5OxfCg8hGr3nTSbL1OhVDRrFZwvF+/R9gY6JRkDUpUA==",
+      "dev": true,
+      "requires": {
+        "@jest/test-result": "^24.7.1",
+        "jest-haste-map": "^24.7.1",
+        "jest-runner": "^24.7.1",
+        "jest-runtime": "^24.7.1"
+      }
+    },
+    "@jest/transform": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.7.1.tgz",
+      "integrity": "sha512-EsOUqP9ULuJ66IkZQhI5LufCHlTbi7hrcllRMUEV/tOgqBVQi93+9qEvkX0n8mYpVXQ8VjwmICeRgg58mrtIEw==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.1.0",
+        "@jest/types": "^24.7.0",
+        "babel-plugin-istanbul": "^5.1.0",
+        "chalk": "^2.0.1",
+        "convert-source-map": "^1.4.0",
+        "fast-json-stable-stringify": "^2.0.0",
+        "graceful-fs": "^4.1.15",
+        "jest-haste-map": "^24.7.1",
+        "jest-regex-util": "^24.3.0",
+        "jest-util": "^24.7.1",
+        "micromatch": "^3.1.10",
+        "realpath-native": "^1.1.0",
+        "slash": "^2.0.0",
+        "source-map": "^0.6.1",
+        "write-file-atomic": "2.4.1"
+      }
+    },
+    "@jest/types": {
+      "version": "24.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.7.0.tgz",
+      "integrity": "sha512-ipJUa2rFWiKoBqMKP63Myb6h9+iT3FHRTF2M8OR6irxWzItisa8i4dcSg14IbvmXUnBlHBlUQPYUHWyX3UPpYA==",
+      "dev": true,
+      "requires": {
+        "@types/istanbul-lib-coverage": "^2.0.0",
+        "@types/yargs": "^12.0.9"
+      }
+    },
+    "@types/babel__core": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.1.tgz",
+      "integrity": "sha512-+hjBtgcFPYyCTo0A15+nxrCVJL7aC6Acg87TXd5OW3QhHswdrOLoles+ldL2Uk8q++7yIfl4tURtztccdeeyOw==",
+      "dev": true,
+      "requires": {
+        "@babel/parser": "^7.1.0",
+        "@babel/types": "^7.0.0",
+        "@types/babel__generator": "*",
+        "@types/babel__template": "*",
+        "@types/babel__traverse": "*"
+      }
+    },
+    "@types/babel__generator": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz",
+      "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "@types/babel__template": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz",
+      "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==",
+      "dev": true,
+      "requires": {
+        "@babel/parser": "^7.1.0",
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "@types/babel__traverse": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz",
+      "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.3.0"
+      }
+    },
+    "@types/istanbul-lib-coverage": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz",
+      "integrity": "sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg==",
+      "dev": true
+    },
+    "@types/stack-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
+      "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==",
+      "dev": true
+    },
+    "@types/yargs": {
+      "version": "12.0.12",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz",
+      "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==",
+      "dev": true
+    },
+    "abab": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz",
+      "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==",
+      "dev": true
+    },
     "abbrev": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -36,12 +451,28 @@
       "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==",
       "dev": true
     },
+    "acorn-globals": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.1.tgz",
+      "integrity": "sha512-gJSiKY8dBIjV/0jagZIFBdVMtfQyA5QHCvAT48H2q8REQoW8Fs5AOjqBql1LgSXgrMWdevcE+8cdZ33NtVbIBA==",
+      "dev": true,
+      "requires": {
+        "acorn": "^6.0.1",
+        "acorn-walk": "^6.0.1"
+      }
+    },
     "acorn-jsx": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
       "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
       "dev": true
     },
+    "acorn-walk": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz",
+      "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==",
+      "dev": true
+    },
     "ajv": {
       "version": "6.6.2",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
@@ -75,6 +506,25 @@
         "color-convert": "^1.9.0"
       }
     },
+    "anymatch": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+      "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+      "dev": true,
+      "requires": {
+        "micromatch": "^3.1.4",
+        "normalize-path": "^2.1.1"
+      }
+    },
+    "append-transform": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
+      "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
+      "dev": true,
+      "requires": {
+        "default-require-extensions": "^2.0.0"
+      }
+    },
     "aproba": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@@ -108,12 +558,48 @@
         }
       }
     },
+    "arr-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+      "dev": true
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+      "dev": true
+    },
+    "arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+      "dev": true
+    },
+    "array-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+      "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+      "dev": true
+    },
     "array-find-index": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
       "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
       "dev": true
     },
+    "array-unique": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+      "dev": true
+    },
+    "arrify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+      "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+      "dev": true
+    },
     "asap": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
@@ -135,6 +621,12 @@
       "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
       "dev": true
     },
+    "assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+      "dev": true
+    },
     "astral-regex": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
@@ -147,12 +639,24 @@
       "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
       "dev": true
     },
+    "async-limiter": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
+      "dev": true
+    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
       "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
       "dev": true
     },
+    "atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+      "dev": true
+    },
     "aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@@ -165,38 +669,189 @@
       "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
       "dev": true
     },
-    "balanced-match": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-      "dev": true
-    },
-    "base64-js": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
-      "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
-    },
-    "bcrypt-pbkdf": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+    "babel-jest": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.7.1.tgz",
+      "integrity": "sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg==",
       "dev": true,
       "requires": {
-        "tweetnacl": "^0.14.3"
+        "@jest/transform": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "@types/babel__core": "^7.1.0",
+        "babel-plugin-istanbul": "^5.1.0",
+        "babel-preset-jest": "^24.6.0",
+        "chalk": "^2.4.2",
+        "slash": "^2.0.0"
       }
     },
-    "bindings": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz",
-      "integrity": "sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew=="
+    "babel-plugin-istanbul": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz",
+      "integrity": "sha512-RNNVv2lsHAXJQsEJ5jonQwrJVWK8AcZpG1oxhnjCUaAjL7xahYLANhPUZbzEQHjKy1NMYUwn+0NPKQc8iSY4xQ==",
+      "dev": true,
+      "requires": {
+        "find-up": "^3.0.0",
+        "istanbul-lib-instrument": "^3.0.0",
+        "test-exclude": "^5.0.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+          "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+          "dev": true,
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "p-try": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+          "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+          "dev": true
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        }
+      }
     },
-    "block-stream": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
-      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+    "babel-plugin-jest-hoist": {
+      "version": "24.6.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz",
+      "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==",
       "dev": true,
       "requires": {
-        "inherits": "~2.0.0"
+        "@types/babel__traverse": "^7.0.6"
+      }
+    },
+    "babel-preset-jest": {
+      "version": "24.6.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz",
+      "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==",
+      "dev": true,
+      "requires": {
+        "@babel/plugin-syntax-object-rest-spread": "^7.0.0",
+        "babel-plugin-jest-hoist": "^24.6.0"
+      }
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "dev": true,
+      "requires": {
+        "cache-base": "^1.0.1",
+        "class-utils": "^0.3.5",
+        "component-emitter": "^1.2.1",
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.1",
+        "mixin-deep": "^1.2.0",
+        "pascalcase": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "base64-js": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
+      "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "dev": true,
+      "requires": {
+        "tweetnacl": "^0.14.3"
+      }
+    },
+    "bindings": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz",
+      "integrity": "sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew=="
+    },
+    "block-stream": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
+      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+      "dev": true,
+      "requires": {
+        "inherits": "~2.0.0"
       }
     },
     "brace-expansion": {
@@ -209,12 +864,96 @@
         "concat-map": "0.0.1"
       }
     },
+    "braces": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+      "dev": true,
+      "requires": {
+        "arr-flatten": "^1.1.0",
+        "array-unique": "^0.3.2",
+        "extend-shallow": "^2.0.1",
+        "fill-range": "^4.0.0",
+        "isobject": "^3.0.1",
+        "repeat-element": "^1.1.2",
+        "snapdragon": "^0.8.1",
+        "snapdragon-node": "^2.0.1",
+        "split-string": "^3.0.2",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "browser-process-hrtime": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
+      "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==",
+      "dev": true
+    },
+    "browser-resolve": {
+      "version": "1.11.3",
+      "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
+      "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
+      "dev": true,
+      "requires": {
+        "resolve": "1.1.7"
+      },
+      "dependencies": {
+        "resolve": {
+          "version": "1.1.7",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+          "dev": true
+        }
+      }
+    },
+    "bser": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz",
+      "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=",
+      "dev": true,
+      "requires": {
+        "node-int64": "^0.4.0"
+      }
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "dev": true
+    },
     "builtin-modules": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
       "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
       "dev": true
     },
+    "cache-base": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+      "dev": true,
+      "requires": {
+        "collection-visit": "^1.0.0",
+        "component-emitter": "^1.2.1",
+        "get-value": "^2.0.6",
+        "has-value": "^1.0.0",
+        "isobject": "^3.0.1",
+        "set-value": "^2.0.0",
+        "to-object-path": "^0.3.0",
+        "union-value": "^1.0.0",
+        "unset-value": "^1.0.0"
+      }
+    },
     "callsites": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
@@ -237,6 +976,15 @@
         "map-obj": "^1.0.0"
       }
     },
+    "capture-exit": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
+      "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==",
+      "dev": true,
+      "requires": {
+        "rsvp": "^4.8.4"
+      }
+    },
     "caseless": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@@ -260,6 +1008,12 @@
       "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
       "dev": true
     },
+    "ci-info": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+      "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+      "dev": true
+    },
     "circular-json": {
       "version": "0.3.3",
       "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
@@ -277,6 +1031,29 @@
         "resolve": "^1.1.6"
       }
     },
+    "class-utils": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "define-property": "^0.2.5",
+        "isobject": "^3.0.0",
+        "static-extend": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
     "cli-cursor": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@@ -292,6 +1069,23 @@
       "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
       "dev": true
     },
+    "cliui": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
+      "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+      "dev": true,
+      "requires": {
+        "string-width": "^2.1.1",
+        "strip-ansi": "^4.0.0",
+        "wrap-ansi": "^2.0.0"
+      }
+    },
+    "co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+      "dev": true
+    },
     "code-point-at": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -304,6 +1098,16 @@
       "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
       "dev": true
     },
+    "collection-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "dev": true,
+      "requires": {
+        "map-visit": "^1.0.0",
+        "object-visit": "^1.0.0"
+      }
+    },
     "color-convert": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -339,6 +1143,18 @@
       "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
       "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
     },
+    "compare-versions": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz",
+      "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==",
+      "dev": true
+    },
+    "component-emitter": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+      "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+      "dev": true
+    },
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -357,6 +1173,21 @@
       "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
       "dev": true
     },
+    "convert-source-map": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
+      "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.1"
+      }
+    },
+    "copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+      "dev": true
+    },
     "core-util-is": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -376,6 +1207,21 @@
         "which": "^1.2.9"
       }
     },
+    "cssom": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz",
+      "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==",
+      "dev": true
+    },
+    "cssstyle": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz",
+      "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==",
+      "dev": true,
+      "requires": {
+        "cssom": "0.3.x"
+      }
+    },
     "currently-unhandled": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@@ -394,6 +1240,30 @@
         "assert-plus": "^1.0.0"
       }
     },
+    "data-urls": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
+      "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
+      "dev": true,
+      "requires": {
+        "abab": "^2.0.0",
+        "whatwg-mimetype": "^2.2.0",
+        "whatwg-url": "^7.0.0"
+      },
+      "dependencies": {
+        "whatwg-url": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
+          "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
+          "dev": true,
+          "requires": {
+            "lodash.sortby": "^4.7.0",
+            "tr46": "^1.0.1",
+            "webidl-conversions": "^4.0.2"
+          }
+        }
+      }
+    },
     "dateformat": {
       "version": "1.0.12",
       "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
@@ -425,12 +1295,27 @@
       "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
       "dev": true
     },
+    "decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "dev": true
+    },
     "deep-is": {
       "version": "0.1.3",
       "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
       "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
       "dev": true
     },
+    "default-require-extensions": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz",
+      "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=",
+      "dev": true,
+      "requires": {
+        "strip-bom": "^3.0.0"
+      }
+    },
     "define-properties": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@@ -440,6 +1325,47 @@
         "object-keys": "^1.0.12"
       }
     },
+    "define-property": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+      "dev": true,
+      "requires": {
+        "is-descriptor": "^1.0.2",
+        "isobject": "^3.0.1"
+      },
+      "dependencies": {
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
     "delay": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/delay/-/delay-4.1.0.tgz",
@@ -457,6 +1383,12 @@
       "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
       "dev": true
     },
+    "detect-newline": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
+      "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
+      "dev": true
+    },
     "dezalgo": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
@@ -467,6 +1399,12 @@
         "wrappy": "1"
       }
     },
+    "diff-sequences": {
+      "version": "24.3.0",
+      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz",
+      "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==",
+      "dev": true
+    },
     "doctrine": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -476,6 +1414,15 @@
         "esutils": "^2.0.2"
       }
     },
+    "domexception": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
+      "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+      "dev": true,
+      "requires": {
+        "webidl-conversions": "^4.0.2"
+      }
+    },
     "ecc-jsbn": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -486,6 +1433,15 @@
         "safer-buffer": "^2.1.0"
       }
     },
+    "end-of-stream": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+      "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+      "dev": true,
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
     "error-ex": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -526,6 +1482,27 @@
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
       "dev": true
     },
+    "escodegen": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz",
+      "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==",
+      "dev": true,
+      "requires": {
+        "esprima": "^3.1.3",
+        "estraverse": "^4.2.0",
+        "esutils": "^2.0.2",
+        "optionator": "^0.8.1",
+        "source-map": "~0.6.1"
+      },
+      "dependencies": {
+        "esprima": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
+          "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
+          "dev": true
+        }
+      }
+    },
     "eslint": {
       "version": "5.12.0",
       "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.12.0.tgz",
@@ -762,46 +1739,217 @@
       "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
       "dev": true
     },
-    "exit": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
-      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
-      "dev": true
-    },
-    "extend": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+    "exec-sh": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz",
+      "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==",
       "dev": true
     },
-    "external-editor": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
-      "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+    "execa": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
       "dev": true,
       "requires": {
-        "chardet": "^0.7.0",
-        "iconv-lite": "^0.4.24",
-        "tmp": "^0.0.33"
+        "cross-spawn": "^6.0.0",
+        "get-stream": "^4.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
       }
     },
-    "extsprintf": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
-      "dev": true
-    },
-    "fast-deep-equal": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+    "exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
       "dev": true
     },
-    "fast-json-stable-stringify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
-      "dev": true
+    "expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+      "dev": true,
+      "requires": {
+        "debug": "^2.3.3",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "posix-character-classes": "^0.1.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "expect": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/expect/-/expect-24.7.1.tgz",
+      "integrity": "sha512-mGfvMTPduksV3xoI0xur56pQsg2vJjNf5+a+bXOjqCkiCBbmCayrBbHS/75y9K430cfqyocPr2ZjiNiRx4SRKw==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0",
+        "ansi-styles": "^3.2.0",
+        "jest-get-type": "^24.3.0",
+        "jest-matcher-utils": "^24.7.0",
+        "jest-message-util": "^24.7.1",
+        "jest-regex-util": "^24.3.0"
+      }
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true
+    },
+    "extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "dev": true,
+      "requires": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "external-editor": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
+      "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+      "dev": true,
+      "requires": {
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
+      }
+    },
+    "extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "dev": true,
+      "requires": {
+        "array-unique": "^0.3.2",
+        "define-property": "^1.0.0",
+        "expand-brackets": "^2.1.4",
+        "extend-shallow": "^2.0.1",
+        "fragment-cache": "^0.2.1",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "dev": true
+    },
+    "fast-deep-equal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+      "dev": true
     },
     "fast-levenshtein": {
       "version": "2.0.6",
@@ -809,6 +1957,15 @@
       "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
       "dev": true
     },
+    "fb-watchman": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz",
+      "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=",
+      "dev": true,
+      "requires": {
+        "bser": "^2.0.0"
+      }
+    },
     "figures": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
@@ -828,6 +1985,39 @@
         "object-assign": "^4.0.1"
       }
     },
+    "fileset": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
+      "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=",
+      "dev": true,
+      "requires": {
+        "glob": "^7.0.3",
+        "minimatch": "^3.0.3"
+      }
+    },
+    "fill-range": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1",
+        "to-regex-range": "^2.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
     "find-up": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
@@ -874,6 +2064,12 @@
         "write": "^0.2.1"
       }
     },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+      "dev": true
+    },
     "forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -891,12 +2087,569 @@
         "mime-types": "^2.1.12"
       }
     },
+    "fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "dev": true,
+      "requires": {
+        "map-cache": "^0.2.2"
+      }
+    },
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
       "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
       "dev": true
     },
+    "fsevents": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
+      "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "nan": "^2.9.2",
+        "node-pre-gyp": "^0.10.0"
+      },
+      "dependencies": {
+        "abbrev": {
+          "version": "1.1.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "ansi-regex": {
+          "version": "2.1.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "aproba": {
+          "version": "1.2.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "are-we-there-yet": {
+          "version": "1.1.5",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "delegates": "^1.0.0",
+            "readable-stream": "^2.0.6"
+          }
+        },
+        "balanced-match": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "brace-expansion": {
+          "version": "1.1.11",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "chownr": {
+          "version": "1.1.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "code-point-at": {
+          "version": "1.1.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "concat-map": {
+          "version": "0.0.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "console-control-strings": {
+          "version": "1.1.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "core-util-is": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "debug": {
+          "version": "2.6.9",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "deep-extend": {
+          "version": "0.6.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "delegates": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "detect-libc": {
+          "version": "1.0.3",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "fs-minipass": {
+          "version": "1.2.5",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "minipass": "^2.2.1"
+          }
+        },
+        "fs.realpath": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "gauge": {
+          "version": "2.7.4",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "aproba": "^1.0.3",
+            "console-control-strings": "^1.0.0",
+            "has-unicode": "^2.0.0",
+            "object-assign": "^4.1.0",
+            "signal-exit": "^3.0.0",
+            "string-width": "^1.0.1",
+            "strip-ansi": "^3.0.1",
+            "wide-align": "^1.1.0"
+          }
+        },
+        "glob": {
+          "version": "7.1.3",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "has-unicode": {
+          "version": "2.0.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "iconv-lite": {
+          "version": "0.4.24",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3"
+          }
+        },
+        "ignore-walk": {
+          "version": "3.0.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "minimatch": "^3.0.4"
+          }
+        },
+        "inflight": {
+          "version": "1.0.6",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "once": "^1.3.0",
+            "wrappy": "1"
+          }
+        },
+        "inherits": {
+          "version": "2.0.3",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "ini": {
+          "version": "1.3.5",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "number-is-nan": "^1.0.0"
+          }
+        },
+        "isarray": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "minimatch": {
+          "version": "3.0.4",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        },
+        "minimist": {
+          "version": "0.0.8",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "minipass": {
+          "version": "2.3.5",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.0"
+          }
+        },
+        "minizlib": {
+          "version": "1.2.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "minipass": "^2.2.1"
+          }
+        },
+        "mkdirp": {
+          "version": "0.5.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "minimist": "0.0.8"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "needle": {
+          "version": "2.2.4",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "debug": "^2.1.2",
+            "iconv-lite": "^0.4.4",
+            "sax": "^1.2.4"
+          }
+        },
+        "node-pre-gyp": {
+          "version": "0.10.3",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "detect-libc": "^1.0.2",
+            "mkdirp": "^0.5.1",
+            "needle": "^2.2.1",
+            "nopt": "^4.0.1",
+            "npm-packlist": "^1.1.6",
+            "npmlog": "^4.0.2",
+            "rc": "^1.2.7",
+            "rimraf": "^2.6.1",
+            "semver": "^5.3.0",
+            "tar": "^4"
+          }
+        },
+        "nopt": {
+          "version": "4.0.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "abbrev": "1",
+            "osenv": "^0.1.4"
+          }
+        },
+        "npm-bundled": {
+          "version": "1.0.5",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "npm-packlist": {
+          "version": "1.2.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ignore-walk": "^3.0.1",
+            "npm-bundled": "^1.0.1"
+          }
+        },
+        "npmlog": {
+          "version": "4.1.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "are-we-there-yet": "~1.1.2",
+            "console-control-strings": "~1.1.0",
+            "gauge": "~2.7.3",
+            "set-blocking": "~2.0.0"
+          }
+        },
+        "number-is-nan": {
+          "version": "1.0.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "object-assign": {
+          "version": "4.1.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "once": {
+          "version": "1.4.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "wrappy": "1"
+          }
+        },
+        "os-homedir": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "os-tmpdir": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "osenv": {
+          "version": "0.1.5",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "os-homedir": "^1.0.0",
+            "os-tmpdir": "^1.0.0"
+          }
+        },
+        "path-is-absolute": {
+          "version": "1.0.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "process-nextick-args": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "rc": {
+          "version": "1.2.8",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "deep-extend": "^0.6.0",
+            "ini": "~1.3.0",
+            "minimist": "^1.2.0",
+            "strip-json-comments": "~2.0.1"
+          },
+          "dependencies": {
+            "minimist": {
+              "version": "1.2.0",
+              "bundled": true,
+              "dev": true,
+              "optional": true
+            }
+          }
+        },
+        "readable-stream": {
+          "version": "2.3.6",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "rimraf": {
+          "version": "2.6.3",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "safer-buffer": {
+          "version": "2.1.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "sax": {
+          "version": "1.2.4",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "semver": {
+          "version": "5.6.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "set-blocking": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "signal-exit": {
+          "version": "3.0.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "code-point-at": "^1.0.0",
+            "is-fullwidth-code-point": "^1.0.0",
+            "strip-ansi": "^3.0.0"
+          }
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ansi-regex": "^2.0.0"
+          }
+        },
+        "strip-json-comments": {
+          "version": "2.0.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "tar": {
+          "version": "4.4.8",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "chownr": "^1.1.1",
+            "fs-minipass": "^1.2.5",
+            "minipass": "^2.3.4",
+            "minizlib": "^1.1.1",
+            "mkdirp": "^0.5.0",
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.2"
+          }
+        },
+        "util-deprecate": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "wide-align": {
+          "version": "1.1.3",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "string-width": "^1.0.2 || 2"
+          }
+        },
+        "wrappy": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        },
+        "yallist": {
+          "version": "3.0.3",
+          "bundled": true,
+          "dev": true,
+          "optional": true
+        }
+      }
+    },
     "fstream": {
       "version": "1.0.11",
       "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
@@ -974,12 +2727,33 @@
         }
       }
     },
+    "get-caller-file": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+      "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+      "dev": true
+    },
     "get-stdin": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
       "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
       "dev": true
     },
+    "get-stream": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+      "dev": true,
+      "requires": {
+        "pump": "^3.0.0"
+      }
+    },
+    "get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+      "dev": true
+    },
     "getobject": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
@@ -1033,6 +2807,12 @@
       "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
       "dev": true
     },
+    "growly": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
+      "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
+      "dev": true
+    },
     "grunt": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.4.tgz",
@@ -1144,6 +2924,18 @@
         "license-checker": "2.0.1"
       }
     },
+    "handlebars": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
+      "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
+      "dev": true,
+      "requires": {
+        "neo-async": "^2.6.0",
+        "optimist": "^0.6.1",
+        "source-map": "^0.6.1",
+        "uglify-js": "^3.1.4"
+      }
+    },
     "har-schema": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -1204,6 +2996,38 @@
       "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
       "dev": true
     },
+    "has-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+      "dev": true,
+      "requires": {
+        "get-value": "^2.0.6",
+        "has-values": "^1.0.0",
+        "isobject": "^3.0.0"
+      }
+    },
+    "has-values": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+      "dev": true,
+      "requires": {
+        "is-number": "^3.0.0",
+        "kind-of": "^4.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
     "hdr-histogram-js": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-1.1.4.tgz",
@@ -1225,6 +3049,15 @@
       "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
       "dev": true
     },
+    "html-encoding-sniffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
+      "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+      "dev": true,
+      "requires": {
+        "whatwg-encoding": "^1.0.1"
+      }
+    },
     "http-signature": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -1261,6 +3094,76 @@
         "resolve-from": "^4.0.0"
       }
     },
+    "import-local": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+      "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+      "dev": true,
+      "requires": {
+        "pkg-dir": "^3.0.0",
+        "resolve-cwd": "^2.0.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+          "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+          "dev": true,
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "p-try": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+          "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+          "dev": true
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        },
+        "pkg-dir": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+          "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+          "dev": true,
+          "requires": {
+            "find-up": "^3.0.0"
+          }
+        }
+      }
+    },
     "imurmurhash": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -1330,12 +3233,53 @@
         }
       }
     },
+    "invariant": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+      "dev": true,
+      "requires": {
+        "loose-envify": "^1.0.0"
+      }
+    },
+    "invert-kv": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+      "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+      "dev": true
+    },
+    "is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
     "is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
       "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
       "dev": true
     },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+      "dev": true
+    },
     "is-builtin-module": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
@@ -1351,12 +3295,66 @@
       "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
       "dev": true
     },
+    "is-ci": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+      "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+      "dev": true,
+      "requires": {
+        "ci-info": "^2.0.0"
+      }
+    },
+    "is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
     "is-date-object": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
       "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
       "dev": true
     },
+    "is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "requires": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+          "dev": true
+        }
+      }
+    },
+    "is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+      "dev": true
+    },
     "is-finite": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
@@ -1372,6 +3370,41 @@
       "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
       "dev": true
     },
+    "is-generator-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+      "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+      "dev": true
+    },
+    "is-number": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
     "is-promise": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
@@ -1384,48 +3417,602 @@
       "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
       "dev": true,
       "requires": {
-        "has": "^1.0.1"
+        "has": "^1.0.1"
+      }
+    },
+    "is-stream": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+      "dev": true
+    },
+    "is-symbol": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+      "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+      "dev": true,
+      "requires": {
+        "has-symbols": "^1.0.0"
+      }
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "dev": true
+    },
+    "is-utf8": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+      "dev": true
+    },
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+      "dev": true
+    },
+    "is-wsl": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+      "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+      "dev": true
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+      "dev": true
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+      "dev": true
+    },
+    "isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+      "dev": true
+    },
+    "istanbul-api": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz",
+      "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==",
+      "dev": true,
+      "requires": {
+        "async": "^2.6.1",
+        "compare-versions": "^3.2.1",
+        "fileset": "^2.0.3",
+        "istanbul-lib-coverage": "^2.0.3",
+        "istanbul-lib-hook": "^2.0.3",
+        "istanbul-lib-instrument": "^3.1.0",
+        "istanbul-lib-report": "^2.0.4",
+        "istanbul-lib-source-maps": "^3.0.2",
+        "istanbul-reports": "^2.1.1",
+        "js-yaml": "^3.12.0",
+        "make-dir": "^1.3.0",
+        "minimatch": "^3.0.4",
+        "once": "^1.4.0"
+      },
+      "dependencies": {
+        "async": {
+          "version": "2.6.2",
+          "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+          "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
+          "dev": true,
+          "requires": {
+            "lodash": "^4.17.11"
+          }
+        }
+      }
+    },
+    "istanbul-lib-coverage": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
+      "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==",
+      "dev": true
+    },
+    "istanbul-lib-hook": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz",
+      "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==",
+      "dev": true,
+      "requires": {
+        "append-transform": "^1.0.0"
+      }
+    },
+    "istanbul-lib-instrument": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz",
+      "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==",
+      "dev": true,
+      "requires": {
+        "@babel/generator": "^7.0.0",
+        "@babel/parser": "^7.0.0",
+        "@babel/template": "^7.0.0",
+        "@babel/traverse": "^7.0.0",
+        "@babel/types": "^7.0.0",
+        "istanbul-lib-coverage": "^2.0.3",
+        "semver": "^5.5.0"
+      }
+    },
+    "istanbul-lib-report": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz",
+      "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==",
+      "dev": true,
+      "requires": {
+        "istanbul-lib-coverage": "^2.0.3",
+        "make-dir": "^1.3.0",
+        "supports-color": "^6.0.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "istanbul-lib-source-maps": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz",
+      "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^2.0.3",
+        "make-dir": "^1.3.0",
+        "rimraf": "^2.6.2",
+        "source-map": "^0.6.1"
+      }
+    },
+    "istanbul-reports": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz",
+      "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==",
+      "dev": true,
+      "requires": {
+        "handlebars": "^4.1.0"
+      }
+    },
+    "jest": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest/-/jest-24.7.1.tgz",
+      "integrity": "sha512-AbvRar5r++izmqo5gdbAjTeA6uNRGoNRuj5vHB0OnDXo2DXWZJVuaObiGgtlvhKb+cWy2oYbQSfxv7Q7GjnAtA==",
+      "dev": true,
+      "requires": {
+        "import-local": "^2.0.0",
+        "jest-cli": "^24.7.1"
+      },
+      "dependencies": {
+        "jest-cli": {
+          "version": "24.7.1",
+          "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.7.1.tgz",
+          "integrity": "sha512-32OBoSCVPzcTslGFl6yVCMzB2SqX3IrWwZCY5mZYkb0D2WsogmU3eV2o8z7+gRQa4o4sZPX/k7GU+II7CxM6WQ==",
+          "dev": true,
+          "requires": {
+            "@jest/core": "^24.7.1",
+            "@jest/test-result": "^24.7.1",
+            "@jest/types": "^24.7.0",
+            "chalk": "^2.0.1",
+            "exit": "^0.1.2",
+            "import-local": "^2.0.0",
+            "is-ci": "^2.0.0",
+            "jest-config": "^24.7.1",
+            "jest-util": "^24.7.1",
+            "jest-validate": "^24.7.0",
+            "prompts": "^2.0.1",
+            "realpath-native": "^1.1.0",
+            "yargs": "^12.0.2"
+          }
+        }
+      }
+    },
+    "jest-changed-files": {
+      "version": "24.7.0",
+      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.7.0.tgz",
+      "integrity": "sha512-33BgewurnwSfJrW7T5/ZAXGE44o7swLslwh8aUckzq2e17/2Os1V0QU506ZNik3hjs8MgnEMKNkcud442NCDTw==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0",
+        "execa": "^1.0.0",
+        "throat": "^4.0.0"
+      }
+    },
+    "jest-config": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz",
+      "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.1.0",
+        "@jest/test-sequencer": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "babel-jest": "^24.7.1",
+        "chalk": "^2.0.1",
+        "glob": "^7.1.1",
+        "jest-environment-jsdom": "^24.7.1",
+        "jest-environment-node": "^24.7.1",
+        "jest-get-type": "^24.3.0",
+        "jest-jasmine2": "^24.7.1",
+        "jest-regex-util": "^24.3.0",
+        "jest-resolve": "^24.7.1",
+        "jest-util": "^24.7.1",
+        "jest-validate": "^24.7.0",
+        "micromatch": "^3.1.10",
+        "pretty-format": "^24.7.0",
+        "realpath-native": "^1.1.0"
+      }
+    },
+    "jest-diff": {
+      "version": "24.7.0",
+      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz",
+      "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.0.1",
+        "diff-sequences": "^24.3.0",
+        "jest-get-type": "^24.3.0",
+        "pretty-format": "^24.7.0"
+      }
+    },
+    "jest-docblock": {
+      "version": "24.3.0",
+      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz",
+      "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==",
+      "dev": true,
+      "requires": {
+        "detect-newline": "^2.1.0"
+      }
+    },
+    "jest-each": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz",
+      "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0",
+        "chalk": "^2.0.1",
+        "jest-get-type": "^24.3.0",
+        "jest-util": "^24.7.1",
+        "pretty-format": "^24.7.0"
+      }
+    },
+    "jest-environment-jsdom": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz",
+      "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==",
+      "dev": true,
+      "requires": {
+        "@jest/environment": "^24.7.1",
+        "@jest/fake-timers": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "jest-mock": "^24.7.0",
+        "jest-util": "^24.7.1",
+        "jsdom": "^11.5.1"
+      }
+    },
+    "jest-environment-node": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.7.1.tgz",
+      "integrity": "sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA==",
+      "dev": true,
+      "requires": {
+        "@jest/environment": "^24.7.1",
+        "@jest/fake-timers": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "jest-mock": "^24.7.0",
+        "jest-util": "^24.7.1"
+      }
+    },
+    "jest-get-type": {
+      "version": "24.3.0",
+      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz",
+      "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==",
+      "dev": true
+    },
+    "jest-haste-map": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz",
+      "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0",
+        "anymatch": "^2.0.0",
+        "fb-watchman": "^2.0.0",
+        "fsevents": "^1.2.7",
+        "graceful-fs": "^4.1.15",
+        "invariant": "^2.2.4",
+        "jest-serializer": "^24.4.0",
+        "jest-util": "^24.7.1",
+        "jest-worker": "^24.6.0",
+        "micromatch": "^3.1.10",
+        "sane": "^4.0.3",
+        "walker": "^1.0.7"
+      }
+    },
+    "jest-jasmine2": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz",
+      "integrity": "sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w==",
+      "dev": true,
+      "requires": {
+        "@babel/traverse": "^7.1.0",
+        "@jest/environment": "^24.7.1",
+        "@jest/test-result": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "chalk": "^2.0.1",
+        "co": "^4.6.0",
+        "expect": "^24.7.1",
+        "is-generator-fn": "^2.0.0",
+        "jest-each": "^24.7.1",
+        "jest-matcher-utils": "^24.7.0",
+        "jest-message-util": "^24.7.1",
+        "jest-runtime": "^24.7.1",
+        "jest-snapshot": "^24.7.1",
+        "jest-util": "^24.7.1",
+        "pretty-format": "^24.7.0",
+        "throat": "^4.0.0"
+      }
+    },
+    "jest-leak-detector": {
+      "version": "24.7.0",
+      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.7.0.tgz",
+      "integrity": "sha512-zV0qHKZGXtmPVVzT99CVEcHE9XDf+8LwiE0Ob7jjezERiGVljmqKFWpV2IkG+rkFIEUHFEkMiICu7wnoPM/RoQ==",
+      "dev": true,
+      "requires": {
+        "pretty-format": "^24.7.0"
+      }
+    },
+    "jest-matcher-utils": {
+      "version": "24.7.0",
+      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz",
+      "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.0.1",
+        "jest-diff": "^24.7.0",
+        "jest-get-type": "^24.3.0",
+        "pretty-format": "^24.7.0"
+      }
+    },
+    "jest-message-util": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz",
+      "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "@jest/test-result": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "@types/stack-utils": "^1.0.1",
+        "chalk": "^2.0.1",
+        "micromatch": "^3.1.10",
+        "slash": "^2.0.0",
+        "stack-utils": "^1.0.1"
+      }
+    },
+    "jest-mock": {
+      "version": "24.7.0",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz",
+      "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0"
+      }
+    },
+    "jest-pnp-resolver": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz",
+      "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==",
+      "dev": true
+    },
+    "jest-regex-util": {
+      "version": "24.3.0",
+      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz",
+      "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==",
+      "dev": true
+    },
+    "jest-resolve": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz",
+      "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0",
+        "browser-resolve": "^1.11.3",
+        "chalk": "^2.0.1",
+        "jest-pnp-resolver": "^1.2.1",
+        "realpath-native": "^1.1.0"
+      }
+    },
+    "jest-resolve-dependencies": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.7.1.tgz",
+      "integrity": "sha512-2Eyh5LJB2liNzfk4eo7bD1ZyBbqEJIyyrFtZG555cSWW9xVHxII2NuOkSl1yUYTAYCAmM2f2aIT5A7HzNmubyg==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0",
+        "jest-regex-util": "^24.3.0",
+        "jest-snapshot": "^24.7.1"
+      }
+    },
+    "jest-runner": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.7.1.tgz",
+      "integrity": "sha512-aNFc9liWU/xt+G9pobdKZ4qTeG/wnJrJna3VqunziDNsWT3EBpmxXZRBMKCsNMyfy+A/XHiV+tsMLufdsNdgCw==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^24.7.1",
+        "@jest/environment": "^24.7.1",
+        "@jest/test-result": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "chalk": "^2.4.2",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.1.15",
+        "jest-config": "^24.7.1",
+        "jest-docblock": "^24.3.0",
+        "jest-haste-map": "^24.7.1",
+        "jest-jasmine2": "^24.7.1",
+        "jest-leak-detector": "^24.7.0",
+        "jest-message-util": "^24.7.1",
+        "jest-resolve": "^24.7.1",
+        "jest-runtime": "^24.7.1",
+        "jest-util": "^24.7.1",
+        "jest-worker": "^24.6.0",
+        "source-map-support": "^0.5.6",
+        "throat": "^4.0.0"
+      }
+    },
+    "jest-runtime": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.7.1.tgz",
+      "integrity": "sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^24.7.1",
+        "@jest/environment": "^24.7.1",
+        "@jest/source-map": "^24.3.0",
+        "@jest/transform": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "@types/yargs": "^12.0.2",
+        "chalk": "^2.0.1",
+        "exit": "^0.1.2",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.1.15",
+        "jest-config": "^24.7.1",
+        "jest-haste-map": "^24.7.1",
+        "jest-message-util": "^24.7.1",
+        "jest-mock": "^24.7.0",
+        "jest-regex-util": "^24.3.0",
+        "jest-resolve": "^24.7.1",
+        "jest-snapshot": "^24.7.1",
+        "jest-util": "^24.7.1",
+        "jest-validate": "^24.7.0",
+        "realpath-native": "^1.1.0",
+        "slash": "^2.0.0",
+        "strip-bom": "^3.0.0",
+        "yargs": "^12.0.2"
+      }
+    },
+    "jest-serializer": {
+      "version": "24.4.0",
+      "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz",
+      "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==",
+      "dev": true
+    },
+    "jest-snapshot": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.7.1.tgz",
+      "integrity": "sha512-8Xk5O4p+JsZZn4RCNUS3pxA+ORKpEKepE+a5ejIKrId9CwrVN0NY+vkqEkXqlstA5NMBkNahXkR/4qEBy0t5yA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.0.0",
+        "@jest/types": "^24.7.0",
+        "chalk": "^2.0.1",
+        "expect": "^24.7.1",
+        "jest-diff": "^24.7.0",
+        "jest-matcher-utils": "^24.7.0",
+        "jest-message-util": "^24.7.1",
+        "jest-resolve": "^24.7.1",
+        "mkdirp": "^0.5.1",
+        "natural-compare": "^1.4.0",
+        "pretty-format": "^24.7.0",
+        "semver": "^5.5.0"
+      }
+    },
+    "jest-util": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz",
+      "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^24.7.1",
+        "@jest/fake-timers": "^24.7.1",
+        "@jest/source-map": "^24.3.0",
+        "@jest/test-result": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "callsites": "^3.0.0",
+        "chalk": "^2.0.1",
+        "graceful-fs": "^4.1.15",
+        "is-ci": "^2.0.0",
+        "mkdirp": "^0.5.1",
+        "slash": "^2.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "jest-validate": {
+      "version": "24.7.0",
+      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz",
+      "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0",
+        "camelcase": "^5.0.0",
+        "chalk": "^2.0.1",
+        "jest-get-type": "^24.3.0",
+        "leven": "^2.1.0",
+        "pretty-format": "^24.7.0"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "5.3.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+          "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+          "dev": true
+        }
+      }
+    },
+    "jest-watcher": {
+      "version": "24.7.1",
+      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.7.1.tgz",
+      "integrity": "sha512-Wd6TepHLRHVKLNPacEsBwlp9raeBIO+01xrN24Dek4ggTS8HHnOzYSFnvp+6MtkkJ3KfMzy220KTi95e2rRkrw==",
+      "dev": true,
+      "requires": {
+        "@jest/test-result": "^24.7.1",
+        "@jest/types": "^24.7.0",
+        "@types/yargs": "^12.0.9",
+        "ansi-escapes": "^3.0.0",
+        "chalk": "^2.0.1",
+        "jest-util": "^24.7.1",
+        "string-length": "^2.0.0"
       }
     },
-    "is-symbol": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
-      "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+    "jest-worker": {
+      "version": "24.6.0",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz",
+      "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==",
       "dev": true,
       "requires": {
-        "has-symbols": "^1.0.0"
+        "merge-stream": "^1.0.1",
+        "supports-color": "^6.1.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
-      "dev": true
-    },
-    "is-utf8": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
-      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
-      "dev": true
-    },
-    "isarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-      "dev": true
-    },
-    "isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
-      "dev": true
-    },
-    "isstream": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
-      "dev": true
-    },
     "jju": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
@@ -1454,6 +4041,60 @@
       "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
       "dev": true
     },
+    "jsdom": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
+      "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
+      "dev": true,
+      "requires": {
+        "abab": "^2.0.0",
+        "acorn": "^5.5.3",
+        "acorn-globals": "^4.1.0",
+        "array-equal": "^1.0.0",
+        "cssom": ">= 0.3.2 < 0.4.0",
+        "cssstyle": "^1.0.0",
+        "data-urls": "^1.0.0",
+        "domexception": "^1.0.1",
+        "escodegen": "^1.9.1",
+        "html-encoding-sniffer": "^1.0.2",
+        "left-pad": "^1.3.0",
+        "nwsapi": "^2.0.7",
+        "parse5": "4.0.0",
+        "pn": "^1.1.0",
+        "request": "^2.87.0",
+        "request-promise-native": "^1.0.5",
+        "sax": "^1.2.4",
+        "symbol-tree": "^3.2.2",
+        "tough-cookie": "^2.3.4",
+        "w3c-hr-time": "^1.0.1",
+        "webidl-conversions": "^4.0.2",
+        "whatwg-encoding": "^1.0.3",
+        "whatwg-mimetype": "^2.1.0",
+        "whatwg-url": "^6.4.1",
+        "ws": "^5.2.0",
+        "xml-name-validator": "^3.0.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "5.7.3",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
+          "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
+          "dev": true
+        }
+      }
+    },
+    "jsesc": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+      "dev": true
+    },
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+      "dev": true
+    },
     "json-parse-helpfulerror": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz",
@@ -1487,6 +4128,23 @@
       "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
       "dev": true
     },
+    "json5": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz",
+      "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.0"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
     "jsprim": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@@ -1499,6 +4157,39 @@
         "verror": "1.10.0"
       }
     },
+    "kind-of": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+      "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+      "dev": true
+    },
+    "kleur": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+      "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+      "dev": true
+    },
+    "lcid": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+      "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+      "dev": true,
+      "requires": {
+        "invert-kv": "^2.0.0"
+      }
+    },
+    "left-pad": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
+      "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==",
+      "dev": true
+    },
+    "leven": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
+      "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
+      "dev": true
+    },
     "levn": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -1612,9 +4303,23 @@
     "lodash": {
       "version": "4.17.11",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
-      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
+    },
+    "lodash.sortby": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+      "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
       "dev": true
     },
+    "loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dev": true,
+      "requires": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      }
+    },
     "loud-rejection": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
@@ -1625,12 +4330,81 @@
         "signal-exit": "^3.0.0"
       }
     },
+    "make-dir": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+      "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+      "dev": true,
+      "requires": {
+        "pify": "^3.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
+      }
+    },
+    "makeerror": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
+      "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=",
+      "dev": true,
+      "requires": {
+        "tmpl": "1.0.x"
+      }
+    },
+    "map-age-cleaner": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
+      "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
+      "dev": true,
+      "requires": {
+        "p-defer": "^1.0.0"
+      }
+    },
+    "map-cache": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+      "dev": true
+    },
     "map-obj": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
       "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
       "dev": true
     },
+    "map-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "dev": true,
+      "requires": {
+        "object-visit": "^1.0.0"
+      }
+    },
+    "mem": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
+      "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
+      "dev": true,
+      "requires": {
+        "map-age-cleaner": "^0.1.1",
+        "mimic-fn": "^2.0.0",
+        "p-is-promise": "^2.0.0"
+      },
+      "dependencies": {
+        "mimic-fn": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+          "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+          "dev": true
+        }
+      }
+    },
     "meow": {
       "version": "3.7.0",
       "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
@@ -1711,6 +4485,36 @@
         }
       }
     },
+    "merge-stream": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
+      "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=",
+      "dev": true,
+      "requires": {
+        "readable-stream": "^2.0.1"
+      }
+    },
+    "micromatch": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "braces": "^2.3.1",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "extglob": "^2.0.4",
+        "fragment-cache": "^0.2.1",
+        "kind-of": "^6.0.2",
+        "nanomatch": "^1.2.9",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.2"
+      }
+    },
     "mime-db": {
       "version": "1.37.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
@@ -1747,6 +4551,27 @@
       "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
       "dev": true
     },
+    "mixin-deep": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
+      "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
+      "dev": true,
+      "requires": {
+        "for-in": "^1.0.2",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
     "mkdirp": {
       "version": "0.5.1",
       "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
@@ -1768,6 +4593,32 @@
       "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
       "dev": true
     },
+    "nan": {
+      "version": "2.13.2",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
+      "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==",
+      "dev": true,
+      "optional": true
+    },
+    "nanomatch": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "fragment-cache": "^0.2.1",
+        "is-windows": "^1.0.2",
+        "kind-of": "^6.0.2",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      }
+    },
     "natives": {
       "version": "1.1.6",
       "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz",
@@ -1781,6 +4632,12 @@
       "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
       "dev": true
     },
+    "neo-async": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
+      "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
+      "dev": true
+    },
     "nice-try": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -1821,6 +4678,31 @@
         }
       }
     },
+    "node-int64": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+      "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
+      "dev": true
+    },
+    "node-modules-regexp": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
+      "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=",
+      "dev": true
+    },
+    "node-notifier": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz",
+      "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==",
+      "dev": true,
+      "requires": {
+        "growly": "^1.3.0",
+        "is-wsl": "^1.1.0",
+        "semver": "^5.5.0",
+        "shellwords": "^0.1.1",
+        "which": "^1.3.0"
+      }
+    },
     "nopt": {
       "version": "3.0.6",
       "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -1842,6 +4724,24 @@
         "validate-npm-package-license": "^3.0.1"
       }
     },
+    "normalize-path": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+      "dev": true,
+      "requires": {
+        "remove-trailing-separator": "^1.0.1"
+      }
+    },
+    "npm-run-path": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+      "dev": true,
+      "requires": {
+        "path-key": "^2.0.0"
+      }
+    },
     "npmlog": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
@@ -1860,6 +4760,12 @@
       "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
       "dev": true
     },
+    "nwsapi": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.3.tgz",
+      "integrity": "sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A==",
+      "dev": true
+    },
     "oauth-sign": {
       "version": "0.9.0",
       "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
@@ -1872,12 +4778,52 @@
       "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
       "dev": true
     },
+    "object-copy": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+      "dev": true,
+      "requires": {
+        "copy-descriptor": "^0.1.0",
+        "define-property": "^0.2.5",
+        "kind-of": "^3.0.3"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
     "object-keys": {
       "version": "1.0.12",
       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
       "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
       "dev": true
     },
+    "object-visit": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.0"
+      }
+    },
     "object.assign": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
@@ -1902,6 +4848,25 @@
         "has": "^1.0.3"
       }
     },
+    "object.getownpropertydescriptors": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
+      "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.2",
+        "es-abstract": "^1.5.1"
+      }
+    },
+    "object.pick": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
     "once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1920,6 +4885,24 @@
         "mimic-fn": "^1.0.0"
       }
     },
+    "optimist": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+      "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+      "dev": true,
+      "requires": {
+        "minimist": "~0.0.1",
+        "wordwrap": "~0.0.2"
+      },
+      "dependencies": {
+        "wordwrap": {
+          "version": "0.0.3",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+          "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+          "dev": true
+        }
+      }
+    },
     "optionator": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
@@ -1940,6 +4923,17 @@
       "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
       "dev": true
     },
+    "os-locale": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
+      "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+      "dev": true,
+      "requires": {
+        "execa": "^1.0.0",
+        "lcid": "^2.0.0",
+        "mem": "^4.0.0"
+      }
+    },
     "os-tmpdir": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
@@ -1956,6 +4950,33 @@
         "os-tmpdir": "^1.0.0"
       }
     },
+    "p-defer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+      "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
+      "dev": true
+    },
+    "p-each-series": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz",
+      "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=",
+      "dev": true,
+      "requires": {
+        "p-reduce": "^1.0.0"
+      }
+    },
+    "p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+      "dev": true
+    },
+    "p-is-promise": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
+      "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
+      "dev": true
+    },
     "p-limit": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -1974,6 +4995,12 @@
         "p-limit": "^1.1.0"
       }
     },
+    "p-reduce": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz",
+      "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=",
+      "dev": true
+    },
     "p-try": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
@@ -2003,6 +5030,18 @@
         "error-ex": "^1.2.0"
       }
     },
+    "parse5": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
+      "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
+      "dev": true
+    },
+    "pascalcase": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+      "dev": true
+    },
     "path-exists": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
@@ -2072,6 +5111,15 @@
         "pinkie": "^2.0.0"
       }
     },
+    "pirates": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz",
+      "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==",
+      "dev": true,
+      "requires": {
+        "node-modules-regexp": "^1.0.0"
+      }
+    },
     "pkg-dir": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
@@ -2087,12 +5135,44 @@
       "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
       "dev": true
     },
+    "pn": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
+      "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
+      "dev": true
+    },
+    "posix-character-classes": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+      "dev": true
+    },
     "prelude-ls": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
       "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
       "dev": true
     },
+    "pretty-format": {
+      "version": "24.7.0",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz",
+      "integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^24.7.0",
+        "ansi-regex": "^4.0.0",
+        "ansi-styles": "^3.2.0",
+        "react-is": "^16.8.4"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        }
+      }
+    },
     "process-nextick-args": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
@@ -2105,12 +5185,32 @@
       "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
       "dev": true
     },
+    "prompts": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.4.tgz",
+      "integrity": "sha512-HTzM3UWp/99A0gk51gAegwo1QRYA7xjcZufMNe33rCclFszUYAuHe1fIN/3ZmiHeGPkUsNaRyQm1hHOfM0PKxA==",
+      "dev": true,
+      "requires": {
+        "kleur": "^3.0.2",
+        "sisteransi": "^1.0.0"
+      }
+    },
     "psl": {
       "version": "1.1.31",
       "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
       "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==",
       "dev": true
     },
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
     "punycode": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@@ -2123,6 +5223,12 @@
       "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
       "dev": true
     },
+    "react-is": {
+      "version": "16.8.6",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
+      "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==",
+      "dev": true
+    },
     "read-installed": {
       "version": "3.1.5",
       "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-3.1.5.tgz",
@@ -2269,6 +5375,15 @@
         "once": "^1.3.0"
       }
     },
+    "realpath-native": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz",
+      "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==",
+      "dev": true,
+      "requires": {
+        "util.promisify": "^1.0.0"
+      }
+    },
     "redent": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
@@ -2279,12 +5394,40 @@
         "strip-indent": "^1.0.1"
       }
     },
+    "regex-not": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.2",
+        "safe-regex": "^1.1.0"
+      }
+    },
     "regexpp": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
       "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
       "dev": true
     },
+    "remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+      "dev": true
+    },
+    "repeat-element": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+      "dev": true
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+      "dev": true
+    },
     "repeating": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
@@ -2322,6 +5465,38 @@
         "uuid": "^3.3.2"
       }
     },
+    "request-promise-core": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz",
+      "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.11"
+      }
+    },
+    "request-promise-native": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz",
+      "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==",
+      "dev": true,
+      "requires": {
+        "request-promise-core": "1.1.2",
+        "stealthy-require": "^1.1.1",
+        "tough-cookie": "^2.3.3"
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+      "dev": true
+    },
     "resolve": {
       "version": "1.9.0",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz",
@@ -2331,12 +5506,35 @@
         "path-parse": "^1.0.6"
       }
     },
+    "resolve-cwd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
+      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+      "dev": true,
+      "requires": {
+        "resolve-from": "^3.0.0"
+      },
+      "dependencies": {
+        "resolve-from": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+          "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+          "dev": true
+        }
+      }
+    },
     "resolve-from": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
       "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
       "dev": true
     },
+    "resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+      "dev": true
+    },
     "restore-cursor": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
@@ -2347,6 +5545,12 @@
         "signal-exit": "^3.0.2"
       }
     },
+    "ret": {
+      "version": "0.1.15",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+      "dev": true
+    },
     "rimraf": {
       "version": "2.6.3",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
@@ -2356,6 +5560,12 @@
         "glob": "^7.1.3"
       }
     },
+    "rsvp": {
+      "version": "4.8.4",
+      "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz",
+      "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==",
+      "dev": true
+    },
     "run-async": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
@@ -2380,12 +5590,52 @@
       "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
       "dev": true
     },
+    "safe-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "dev": true,
+      "requires": {
+        "ret": "~0.1.10"
+      }
+    },
     "safer-buffer": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
       "dev": true
     },
+    "sane": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz",
+      "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==",
+      "dev": true,
+      "requires": {
+        "@cnakazawa/watch": "^1.0.3",
+        "anymatch": "^2.0.0",
+        "capture-exit": "^2.0.0",
+        "exec-sh": "^0.3.2",
+        "execa": "^1.0.0",
+        "fb-watchman": "^2.0.0",
+        "micromatch": "^3.1.4",
+        "minimist": "^1.1.1",
+        "walker": "~1.0.5"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
+    "sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+      "dev": true
+    },
     "semver": {
       "version": "5.6.0",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
@@ -2398,6 +5648,29 @@
       "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
       "dev": true
     },
+    "set-value": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
+      "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-extendable": "^0.1.1",
+        "is-plain-object": "^2.0.3",
+        "split-string": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
     "shebang-command": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@@ -2413,12 +5686,30 @@
       "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
       "dev": true
     },
+    "shellwords": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
+      "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
+      "dev": true
+    },
     "signal-exit": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
       "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
       "dev": true
     },
+    "sisteransi": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz",
+      "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==",
+      "dev": true
+    },
+    "slash": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+      "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+      "dev": true
+    },
     "slice-ansi": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.0.0.tgz",
@@ -2436,6 +5727,169 @@
       "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=",
       "dev": true
     },
+    "snapdragon": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+      "dev": true,
+      "requires": {
+        "base": "^0.11.1",
+        "debug": "^2.2.0",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "map-cache": "^0.2.2",
+        "source-map": "^0.5.6",
+        "source-map-resolve": "^0.5.0",
+        "use": "^3.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "snapdragon-node": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.0",
+        "snapdragon-util": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "snapdragon-util": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.2.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true
+    },
+    "source-map-resolve": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
+      "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+      "dev": true,
+      "requires": {
+        "atob": "^2.1.1",
+        "decode-uri-component": "^0.2.0",
+        "resolve-url": "^0.2.1",
+        "source-map-url": "^0.4.0",
+        "urix": "^0.1.0"
+      }
+    },
+    "source-map-support": {
+      "version": "0.5.12",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
+      "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "source-map-url": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+      "dev": true
+    },
     "spdx-correct": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
@@ -2468,6 +5922,15 @@
       "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==",
       "dev": true
     },
+    "split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.0"
+      }
+    },
     "sprintf-js": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
@@ -2491,6 +5954,49 @@
         "tweetnacl": "~0.14.0"
       }
     },
+    "stack-utils": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz",
+      "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==",
+      "dev": true
+    },
+    "static-extend": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+      "dev": true,
+      "requires": {
+        "define-property": "^0.2.5",
+        "object-copy": "^0.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "stealthy-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+      "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+      "dev": true
+    },
+    "string-length": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
+      "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=",
+      "dev": true,
+      "requires": {
+        "astral-regex": "^1.0.0",
+        "strip-ansi": "^4.0.0"
+      }
+    },
     "string-width": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -2525,6 +6031,12 @@
       "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
       "dev": true
     },
+    "strip-eof": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+      "dev": true
+    },
     "strip-indent": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
@@ -2549,6 +6061,12 @@
         "has-flag": "^3.0.0"
       }
     },
+    "symbol-tree": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
+      "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
+      "dev": true
+    },
     "table": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/table/-/table-5.1.1.tgz",
@@ -2572,12 +6090,139 @@
         "inherits": "2"
       }
     },
+    "test-exclude": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz",
+      "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==",
+      "dev": true,
+      "requires": {
+        "arrify": "^1.0.1",
+        "minimatch": "^3.0.4",
+        "read-pkg-up": "^4.0.0",
+        "require-main-filename": "^1.0.1"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "load-json-file": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+          "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "parse-json": "^4.0.0",
+            "pify": "^3.0.0",
+            "strip-bom": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+          "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+          "dev": true,
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "p-try": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+          "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+          "dev": true
+        },
+        "parse-json": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "dev": true,
+          "requires": {
+            "error-ex": "^1.3.1",
+            "json-parse-better-errors": "^1.0.1"
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        },
+        "path-type": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+          "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+          "dev": true,
+          "requires": {
+            "pify": "^3.0.0"
+          }
+        },
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        },
+        "read-pkg": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+          "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+          "dev": true,
+          "requires": {
+            "load-json-file": "^4.0.0",
+            "normalize-package-data": "^2.3.2",
+            "path-type": "^3.0.0"
+          }
+        },
+        "read-pkg-up": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz",
+          "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==",
+          "dev": true,
+          "requires": {
+            "find-up": "^3.0.0",
+            "read-pkg": "^3.0.0"
+          }
+        }
+      }
+    },
     "text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
       "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
       "dev": true
     },
+    "throat": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz",
+      "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=",
+      "dev": true
+    },
     "through": {
       "version": "2.3.8",
       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -2593,6 +6238,60 @@
         "os-tmpdir": "~1.0.2"
       }
     },
+    "tmpl": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
+      "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=",
+      "dev": true
+    },
+    "to-fast-properties": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+      "dev": true
+    },
+    "to-object-path": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "to-regex": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "regex-not": "^1.0.2",
+        "safe-regex": "^1.1.0"
+      }
+    },
+    "to-regex-range": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+      "dev": true,
+      "requires": {
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1"
+      }
+    },
     "tough-cookie": {
       "version": "2.4.3",
       "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
@@ -2611,6 +6310,15 @@
         }
       }
     },
+    "tr46": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+      "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
     "treeify": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
@@ -2623,6 +6331,12 @@
       "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
       "dev": true
     },
+    "trim-right": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+      "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
+      "dev": true
+    },
     "tslib": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
@@ -2653,6 +6367,26 @@
         "prelude-ls": "~1.1.2"
       }
     },
+    "uglify-js": {
+      "version": "3.5.4",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.4.tgz",
+      "integrity": "sha512-GpKo28q/7Bm5BcX9vOu4S46FwisbPbAmkkqPnGIpKvKTM96I85N6XHQV+k4I6FA2wxgLhcsSyHoNhzucwCflvA==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "commander": "~2.20.0",
+        "source-map": "~0.6.1"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.20.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+          "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+          "dev": true,
+          "optional": true
+        }
+      }
+    },
     "underscore.string": {
       "version": "3.3.5",
       "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
@@ -2663,6 +6397,81 @@
         "util-deprecate": "^1.0.2"
       }
     },
+    "union-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
+      "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "get-value": "^2.0.6",
+        "is-extendable": "^0.1.1",
+        "set-value": "^0.4.3"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "set-value": {
+          "version": "0.4.3",
+          "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
+          "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
+          "dev": true,
+          "requires": {
+            "extend-shallow": "^2.0.1",
+            "is-extendable": "^0.1.1",
+            "is-plain-object": "^2.0.1",
+            "to-object-path": "^0.3.0"
+          }
+        }
+      }
+    },
+    "unset-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+      "dev": true,
+      "requires": {
+        "has-value": "^0.3.1",
+        "isobject": "^3.0.0"
+      },
+      "dependencies": {
+        "has-value": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+          "dev": true,
+          "requires": {
+            "get-value": "^2.0.3",
+            "has-values": "^0.1.4",
+            "isobject": "^2.0.0"
+          },
+          "dependencies": {
+            "isobject": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+              "dev": true,
+              "requires": {
+                "isarray": "1.0.0"
+              }
+            }
+          }
+        },
+        "has-values": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+          "dev": true
+        }
+      }
+    },
     "uri-js": {
       "version": "4.2.2",
       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@@ -2672,6 +6481,18 @@
         "punycode": "^2.1.0"
       }
     },
+    "urix": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+      "dev": true
+    },
+    "use": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+      "dev": true
+    },
     "util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -2684,6 +6505,16 @@
       "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=",
       "dev": true
     },
+    "util.promisify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
+      "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.2",
+        "object.getownpropertydescriptors": "^2.0.3"
+      }
+    },
     "uuid": {
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
@@ -2711,6 +6542,56 @@
         "extsprintf": "^1.2.0"
       }
     },
+    "w3c-hr-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
+      "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
+      "dev": true,
+      "requires": {
+        "browser-process-hrtime": "^0.1.2"
+      }
+    },
+    "walker": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz",
+      "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=",
+      "dev": true,
+      "requires": {
+        "makeerror": "1.0.x"
+      }
+    },
+    "webidl-conversions": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+      "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+      "dev": true
+    },
+    "whatwg-encoding": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+      "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+      "dev": true,
+      "requires": {
+        "iconv-lite": "0.4.24"
+      }
+    },
+    "whatwg-mimetype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+      "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+      "dev": true
+    },
+    "whatwg-url": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
+      "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
+      "dev": true,
+      "requires": {
+        "lodash.sortby": "^4.7.0",
+        "tr46": "^1.0.1",
+        "webidl-conversions": "^4.0.2"
+      }
+    },
     "which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -2720,6 +6601,12 @@
         "isexe": "^2.0.0"
       }
     },
+    "which-module": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+      "dev": true
+    },
     "wide-align": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
@@ -2735,6 +6622,53 @@
       "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
       "dev": true
     },
+    "wrap-ansi": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "dev": true,
+      "requires": {
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "dev": true,
+          "requires": {
+            "number-is-nan": "^1.0.0"
+          }
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "dev": true,
+          "requires": {
+            "code-point-at": "^1.0.0",
+            "is-fullwidth-code-point": "^1.0.0",
+            "strip-ansi": "^3.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^2.0.0"
+          }
+        }
+      }
+    },
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -2749,6 +6683,127 @@
       "requires": {
         "mkdirp": "^0.5.1"
       }
+    },
+    "write-file-atomic": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz",
+      "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.11",
+        "imurmurhash": "^0.1.4",
+        "signal-exit": "^3.0.2"
+      }
+    },
+    "ws": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
+      "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
+      "dev": true,
+      "requires": {
+        "async-limiter": "~1.0.0"
+      }
+    },
+    "xml-name-validator": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+      "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+      "dev": true
+    },
+    "y18n": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+      "dev": true
+    },
+    "yargs": {
+      "version": "12.0.5",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
+      "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
+      "dev": true,
+      "requires": {
+        "cliui": "^4.0.0",
+        "decamelize": "^1.2.0",
+        "find-up": "^3.0.0",
+        "get-caller-file": "^1.0.1",
+        "os-locale": "^3.0.0",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^1.0.1",
+        "set-blocking": "^2.0.0",
+        "string-width": "^2.0.0",
+        "which-module": "^2.0.0",
+        "y18n": "^3.2.1 || ^4.0.0",
+        "yargs-parser": "^11.1.1"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+          "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+          "dev": true,
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "p-try": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+          "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+          "dev": true
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        }
+      }
+    },
+    "yargs-parser": {
+      "version": "11.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
+      "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "5.3.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+          "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+          "dev": true
+        }
+      }
     }
   }
 }
diff --git a/package.json b/package.json
index f56db65..f974454 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
     "build": "npm run format && node-gyp rebuild",
     "build:debug": "npm run format && node-gyp rebuild --debug",
     "license:report": "mkdir -p report && grunt grunt-license-report",
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "test": "jest --verbose"
   },
   "repository": {
     "type": "git",
@@ -34,6 +34,7 @@
     "eslint-plugin-import": "^2.14.0",
     "grunt": "^1.0.4",
     "grunt-license-report": "^0.0.8",
+    "jest": "^24.7.1",
     "node-addon-api": "^1.6.2",
     "node-gyp": "^3.8.0"
   },
@@ -41,6 +42,7 @@
     "bindings": "^1.3.1",
     "commander": "^2.19.0",
     "delay": "^4.1.0",
-    "hdr-histogram-js": "^1.1.4"
+    "hdr-histogram-js": "^1.1.4",
+    "lodash": "^4.17.11"
   }
 }
diff --git a/tests/consumer.test.js b/tests/consumer.test.js
new file mode 100644
index 0000000..c008cac
--- /dev/null
+++ b/tests/consumer.test.js
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const Pulsar = require('../index.js');
+
+(() => {
+  describe('Consumer', () => {
+    const client = new Pulsar.Client({
+      serviceUrl: 'pulsar://localhost:6650',
+      operationTimeoutSeconds: 30,
+    });
+    describe('Create', () => {
+      test('No Topic', async () => {
+        await expect(client.subscribe({
+          subscription: 'sub1',
+          ackTimeoutMs: 10000,
+        })).rejects.toThrow('Topic is required and must be specified as a string when creating consumer');
+      });
+
+      test('Not String Topic', async () => {
+        await expect(client.subscribe({
+          topic: 0,
+          subscription: 'sub1',
+          ackTimeoutMs: 10000,
+        })).rejects.toThrow('Topic is required and must be specified as a string when creating consumer');
+      });
+
+      test('No Subscription', async () => {
+        await expect(client.subscribe({
+          topic: 'persistent://public/default/t1',
+          ackTimeoutMs: 10000,
+        })).rejects.toThrow('Subscription is required and must be specified as a string when creating consumer');
+      });
+
+      test('Not String Subscription', async () => {
+        await expect(client.subscribe({
+          topic: 'persistent://public/default/t1',
+          subscription: 0,
+          ackTimeoutMs: 10000,
+        })).rejects.toThrow('Subscription is required and must be specified as a string when creating consumer');
+      });
+
+      test('Not Exist Tenant', async () => {
+        await expect(client.subscribe({
+          topic: 'persistent://no-tenant/namespace/topic',
+          subscription: 'sub1',
+          ackTimeoutMs: 10000,
+        })).rejects.toThrow('Failed to create consumer: ConnectError');
+      });
+
+      test('Not Exist Namespace', async () => {
+        await expect(client.subscribe({
+          topic: 'persistent://public/no-namespace/topic',
+          subscription: 'sub1',
+          ackTimeoutMs: 10000,
+        })).rejects.toThrow('Failed to create consumer: ConnectError');
+      });
+    });
+  });
+})();
diff --git a/tests/end_to_end.test.js b/tests/end_to_end.test.js
new file mode 100644
index 0000000..9d5ed49
--- /dev/null
+++ b/tests/end_to_end.test.js
@@ -0,0 +1,68 @@
+/**
+
+- Licensed to the Apache Software Foundation (ASF) under one
+- or more contributor license agreements.  See the NOTICE file
+- distributed with this work for additional information
+- regarding copyright ownership.  The ASF licenses this file
+- to you under the Apache License, Version 2.0 (the
+- "License"); you may not use this file except in compliance
+- with the License.  You may obtain a copy of the License at
+  *
+- http://www.apache.org/licenses/LICENSE-2.0
+  *
+- Unless required by applicable law or agreed to in writing,
+- software distributed under the License is distributed on an
+- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+- KIND, either express or implied.  See the License for the
+- specific language governing permissions and limitations
+- under the License.
+  */
+
+const lodash = require('lodash');
+const Pulsar = require('../index.js');
+
+(() => {
+  describe('End To End', () => {
+    const client = new Pulsar.Client({
+      serviceUrl: 'pulsar://localhost:6650',
+      operationTimeoutSeconds: 30,
+    });
+    test('Produce/Consume', async () => {
+      const producer = await client.createProducer({
+        topic: 'persistent://public/default/test-end-to-end',
+        sendTimeoutMs: 30000,
+        batchingEnabled: true,
+      });
+      expect(producer).not.toBeNull();
+
+      const consumer = await client.subscribe({
+        topic: 'persistent://public/default/test-end-to-end',
+        subscription: 'sub1',
+        ackTimeoutMs: 10000,
+      });
+      expect(consumer).not.toBeNull();
+
+      const messages = [];
+      for (let i = 0; i < 10; i += 1) {
+        const msg = `my-message-${i}`;
+        producer.send({
+          data: Buffer.from(msg),
+        });
+        messages.push(msg);
+      }
+      await producer.flush();
+
+      const results = [];
+      for (let i = 0; i < 10; i += 1) {
+        const msg = await consumer.receive();
+        consumer.acknowledge(msg);
+        results.push(msg.getData().toString());
+      }
+      expect(lodash.difference(messages, results)).toEqual([]);
+
+      await producer.close();
+      await consumer.close();
+      await client.close();
+    });
+  });
+})();
diff --git a/tests/producer.test.js b/tests/producer.test.js
new file mode 100644
index 0000000..5f8ba52
--- /dev/null
+++ b/tests/producer.test.js
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const Pulsar = require('../index.js');
+
+(() => {
+  describe('Producer', () => {
+    const client = new Pulsar.Client({
+      serviceUrl: 'pulsar://localhost:6650',
+      operationTimeoutSeconds: 30,
+    });
+    describe('Create', () => {
+      test('No Topic', async () => {
+        await expect(client.createProducer({
+          sendTimeoutMs: 30000,
+          batchingEnabled: true,
+        })).rejects.toThrow('Topic is required and must be specified as a string when creating producer');
+      });
+
+      test('Not String Topic', async () => {
+        await expect(client.createProducer({
+          topic: 0,
+          sendTimeoutMs: 30000,
+          batchingEnabled: true,
+        })).rejects.toThrow('Topic is required and must be specified as a string when creating producer');
+      });
+
+      test('Not Exist Tenant', async () => {
+        await expect(client.createProducer({
+          topic: 'persistent://no-tenant/namespace/topic',
+          sendTimeoutMs: 30000,
+          batchingEnabled: true,
+        })).rejects.toThrow('Failed to create producer: ConnectError');
+      });
+
+      test('Not Exist Namespace', async () => {
+        await expect(client.createProducer({
+          topic: 'persistent://public/no-namespace/topic',
+          sendTimeoutMs: 30000,
+          batchingEnabled: true,
+        })).rejects.toThrow('Failed to create producer: ConnectError');
+      });
+    });
+  });
+})();


[pulsar-client-node] 12/36: Update module with severity vulnerabilities

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit e938a7cd14ea6cb6d23649e88a1f4cbbdbe1b9a9
Author: Masahiro Sakamoto <ma...@yahoo-corp.jp>
AuthorDate: Thu Apr 4 13:30:07 2019 +0900

    Update module with severity vulnerabilities
---
 package-lock.json | 44 ++++++++++++++++++--------------------------
 package.json      |  2 +-
 2 files changed, 19 insertions(+), 27 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 0adfd94..f150028 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -98,6 +98,14 @@
       "dev": true,
       "requires": {
         "sprintf-js": "~1.0.2"
+      },
+      "dependencies": {
+        "sprintf-js": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+          "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+          "dev": true
+        }
       }
     },
     "array-find-index": {
@@ -1026,9 +1034,9 @@
       "dev": true
     },
     "grunt": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.3.tgz",
-      "integrity": "sha512-/JzmZNPfKorlCrrmxWqQO4JVodO+DVd5XX4DkocL/1WlLlKVLE9+SdEIempOAxDhWPysLle6afvn/hg7Ck2k9g==",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.4.tgz",
+      "integrity": "sha512-PYsMOrOC+MsdGEkFVwMaMyc6Ob7pKmq+deg1Sjr+vvMWp35sztfwKE7qoN51V+UEtHsyNuMcGdgMLFkBHvMxHQ==",
       "dev": true,
       "requires": {
         "coffeescript": "~1.10.0",
@@ -1042,7 +1050,7 @@
         "grunt-legacy-log": "~2.0.0",
         "grunt-legacy-util": "~1.1.1",
         "iconv-lite": "~0.4.13",
-        "js-yaml": "~3.5.2",
+        "js-yaml": "~3.13.0",
         "minimatch": "~3.0.2",
         "mkdirp": "~0.5.1",
         "nopt": "~3.0.6",
@@ -1050,12 +1058,6 @@
         "rimraf": "~2.6.2"
       },
       "dependencies": {
-        "esprima": {
-          "version": "2.7.3",
-          "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
-          "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
-          "dev": true
-        },
         "glob": {
           "version": "7.0.6",
           "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
@@ -1082,16 +1084,6 @@
             "resolve": "~1.1.0"
           }
         },
-        "js-yaml": {
-          "version": "3.5.5",
-          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz",
-          "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=",
-          "dev": true,
-          "requires": {
-            "argparse": "^1.0.2",
-            "esprima": "^2.6.0"
-          }
-        },
         "resolve": {
           "version": "1.1.7",
           "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
@@ -1447,9 +1439,9 @@
       "dev": true
     },
     "js-yaml": {
-      "version": "3.12.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
-      "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
+      "version": "3.13.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz",
+      "integrity": "sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==",
       "dev": true,
       "requires": {
         "argparse": "^1.0.7",
@@ -2477,9 +2469,9 @@
       "dev": true
     },
     "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+      "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
       "dev": true
     },
     "sshpk": {
diff --git a/package.json b/package.json
index 2455ce6..f56db65 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
     "eslint": "^5.12.0",
     "eslint-config-airbnb-base": "^13.1.0",
     "eslint-plugin-import": "^2.14.0",
-    "grunt": "^1.0.3",
+    "grunt": "^1.0.4",
     "grunt-license-report": "^0.0.8",
     "node-addon-api": "^1.6.2",
     "node-gyp": "^3.8.0"


[pulsar-client-node] 27/36: fix conditions

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 25c1f761c0d06482e7e400b4febf2eb70d9faa30
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Tue Jun 11 19:08:19 2019 +0900

    fix conditions
---
 .vscode/ipch/cc6423c2fd8ec533/mmap_address.bin | Bin 0 -> 8 bytes
 src/Reader.cc                                  |   6 +++---
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.vscode/ipch/cc6423c2fd8ec533/mmap_address.bin b/.vscode/ipch/cc6423c2fd8ec533/mmap_address.bin
new file mode 100644
index 0000000..71307ab
Binary files /dev/null and b/.vscode/ipch/cc6423c2fd8ec533/mmap_address.bin differ
diff --git a/src/Reader.cc b/src/Reader.cc
index cfca247..8118628 100644
--- a/src/Reader.cc
+++ b/src/Reader.cc
@@ -149,10 +149,10 @@ Napi::Value Reader::HasNext(const Napi::CallbackInfo &info) {
   if (result != pulsar_result_Ok) {
     Napi::Error::New(info.Env(), "Failed to check if next message is available").ThrowAsJavaScriptException();
     return Napi::Boolean::New(info.Env(), false);
-  } else if (value != 1) {
-    return Napi::Boolean::New(info.Env(), false);
-  } else {
+  } else if (value == 1) {
     return Napi::Boolean::New(info.Env(), true);
+  } else {
+    return Napi::Boolean::New(info.Env(), false);
   }
 }
 


[pulsar-client-node] 24/36: impl reader

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 618729cc5a3ec00b7d2cb5c6a8e12dd142be203a
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Tue Jun 11 13:47:12 2019 +0900

    impl reader
---
 binding.gyp                        |   2 +
 src/Client.h => examples/reader.js |  38 ++++----
 src/Client.cc                      |  14 ++-
 src/Client.h                       |   1 +
 src/Reader.cc                      | 185 +++++++++++++++++++++++++++++++++++++
 src/{Client.h => Reader.h}         |  22 +++--
 src/ReaderConfig.cc                |  70 ++++++++++++++
 src/{Client.h => ReaderConfig.h}   |  27 +++---
 src/addon.cc                       |   2 +
 tests/end_to_end.test.js           |  61 +++++++++++-
 tests/reader.test.js               |  72 +++++++++++++++
 11 files changed, 445 insertions(+), 49 deletions(-)

diff --git a/binding.gyp b/binding.gyp
index cfcf8b7..4b7b2f4 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -36,6 +36,8 @@
         "src/ProducerConfig.cc",
         "src/Consumer.cc",
         "src/ConsumerConfig.cc",
+        "src/Reader.cc",
+        "src/ReaderConfig.cc",
       ],
       "libraries": ["-lpulsar"],
     }
diff --git a/src/Client.h b/examples/reader.js
similarity index 59%
copy from src/Client.h
copy to examples/reader.js
index 63c66f2..5665a35 100644
--- a/src/Client.h
+++ b/examples/reader.js
@@ -17,25 +17,27 @@
  * under the License.
  */
 
-#ifndef CLIENT_H
-#define CLIENT_H
+const Pulsar = require('../index.js');
 
-#include <napi.h>
-#include <pulsar/c/client.h>
+(async () => {
+  // Create a client
+  const client = new Pulsar.Client({
+    serviceUrl: 'pulsar://localhost:6650',
+    operationTimeoutSeconds: 30,
+  });
 
-class Client : public Napi::ObjectWrap<Client> {
- public:
-  static Napi::Object Init(Napi::Env env, Napi::Object exports);
-  Client(const Napi::CallbackInfo &info);
-  ~Client();
+  // Create a reader
+  const reader = await client.createReader({
+    topic: 'persistent://public/default/my-topic',
+    startMessageId: Pulsar.MessageId.earliest(),
+  });
 
- private:
-  static Napi::FunctionReference constructor;
-  pulsar_client_t *cClient;
+  // read messages
+  for (let i = 0; i < 10; i += 1) {
+    const msg = await reader.readNext();
+    console.log(msg.getData().toString());
+  }
 
-  Napi::Value CreateProducer(const Napi::CallbackInfo &info);
-  Napi::Value Subscribe(const Napi::CallbackInfo &info);
-  Napi::Value Close(const Napi::CallbackInfo &info);
-};
-
-#endif
+  await reader.close();
+  await client.close();
+})();
diff --git a/src/Client.cc b/src/Client.cc
index 123fc0f..a0c5bad 100644
--- a/src/Client.cc
+++ b/src/Client.cc
@@ -20,6 +20,7 @@
 #include "Client.h"
 #include "Consumer.h"
 #include "Producer.h"
+#include "Reader.h"
 #include "Authentication.h"
 #include <pulsar/c/client.h>
 #include <pulsar/c/client_configuration.h>
@@ -43,10 +44,11 @@ Napi::FunctionReference Client::constructor;
 Napi::Object Client::Init(Napi::Env env, Napi::Object exports) {
   Napi::HandleScope scope(env);
 
-  Napi::Function func =
-      DefineClass(env, "Client",
-                  {InstanceMethod("createProducer", &Client::CreateProducer),
-                   InstanceMethod("subscribe", &Client::Subscribe), InstanceMethod("close", &Client::Close)});
+  Napi::Function func = DefineClass(
+      env, "Client",
+      {InstanceMethod("createProducer", &Client::CreateProducer),
+       InstanceMethod("subscribe", &Client::Subscribe), InstanceMethod("createReader", &Client::CreateReader),
+       InstanceMethod("close", &Client::Close)});
 
   constructor = Napi::Persistent(func);
   constructor.SuppressDestruct();
@@ -151,6 +153,10 @@ Napi::Value Client::Subscribe(const Napi::CallbackInfo &info) {
   return Consumer::NewInstance(info, this->cClient);
 }
 
+Napi::Value Client::CreateReader(const Napi::CallbackInfo &info) {
+  return Reader::NewInstance(info, this->cClient);
+}
+
 class ClientCloseWorker : public Napi::AsyncWorker {
  public:
   ClientCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient)
diff --git a/src/Client.h b/src/Client.h
index 63c66f2..0def389 100644
--- a/src/Client.h
+++ b/src/Client.h
@@ -35,6 +35,7 @@ class Client : public Napi::ObjectWrap<Client> {
 
   Napi::Value CreateProducer(const Napi::CallbackInfo &info);
   Napi::Value Subscribe(const Napi::CallbackInfo &info);
+  Napi::Value CreateReader(const Napi::CallbackInfo &info);
   Napi::Value Close(const Napi::CallbackInfo &info);
 };
 
diff --git a/src/Reader.cc b/src/Reader.cc
new file mode 100644
index 0000000..8c2b859
--- /dev/null
+++ b/src/Reader.cc
@@ -0,0 +1,185 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "Message.h"
+#include "Reader.h"
+#include "ReaderConfig.h"
+#include <pulsar/c/result.h>
+#include <pulsar/c/reader.h>
+
+Napi::FunctionReference Reader::constructor;
+
+void Reader::Init(Napi::Env env, Napi::Object exports) {
+  Napi::HandleScope scope(env);
+
+  Napi::Function func = DefineClass(env, "Reader",
+                                    {
+                                        InstanceMethod("readNext", &Reader::ReadNext),
+                                        InstanceMethod("hasNext", &Reader::HasNext),
+                                        InstanceMethod("close", &Reader::Close),
+                                    });
+
+  constructor = Napi::Persistent(func);
+  constructor.SuppressDestruct();
+}
+
+void Reader::SetCReader(pulsar_reader_t *cReader) { this->cReader = cReader; }
+
+Reader::Reader(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Reader>(info) {}
+
+class ReaderNewInstanceWorker : public Napi::AsyncWorker {
+ public:
+  ReaderNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
+                          ReaderConfig *readerConfig)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cClient(cClient),
+        readerConfig(readerConfig) {}
+  ~ReaderNewInstanceWorker() {}
+  void Execute() {
+    const std::string &topic = this->readerConfig->GetTopic();
+    if (topic.empty()) {
+      SetError(std::string("Topic is required and must be specified as a string when creating reader"));
+      return;
+    }
+    if (this->readerConfig->GetCStartMessageId() == nullptr) {
+      SetError(std::string(
+          "StartMessageId is required and must be specified as a MessageId object when creating reader"));
+      return;
+    }
+
+    pulsar_result result =
+        pulsar_client_create_reader(this->cClient, topic.c_str(), this->readerConfig->GetCStartMessageId(),
+                                    this->readerConfig->GetCReaderConfig(), &(this->cReader));
+    delete this->readerConfig;
+    if (result != pulsar_result_Ok) {
+      SetError(std::string("Failed to create reader: ") + pulsar_result_str(result));
+      return;
+    }
+  }
+  void OnOK() {
+    Napi::Object obj = Reader::constructor.New({});
+    Reader *reader = Reader::Unwrap(obj);
+    reader->SetCReader(this->cReader);
+    this->deferred.Resolve(obj);
+  }
+  void OnError(const Napi::Error &e) { this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value()); }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_client_t *cClient;
+  ReaderConfig *readerConfig;
+  pulsar_reader_t *cReader;
+};
+
+Napi::Value Reader::NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient) {
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  Napi::Object config = info[0].As<Napi::Object>();
+  ReaderConfig *readerConfig = new ReaderConfig(config);
+  ReaderNewInstanceWorker *wk = new ReaderNewInstanceWorker(deferred, cClient, readerConfig);
+  wk->Queue();
+  return deferred.Promise();
+}
+
+class ReaderReadNextWorker : public Napi::AsyncWorker {
+ public:
+  ReaderReadNextWorker(const Napi::Promise::Deferred &deferred, pulsar_reader_t *cReader,
+                       int64_t timeout = -1)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cReader(cReader),
+        timeout(timeout) {}
+  ~ReaderReadNextWorker() {}
+  void Execute() {
+    pulsar_result result;
+    if (timeout > 0) {
+      result = pulsar_reader_read_next_with_timeout(this->cReader, &(this->cMessage), timeout);
+    } else {
+      result = pulsar_reader_read_next(this->cReader, &(this->cMessage));
+    }
+    if (result != pulsar_result_Ok) {
+      SetError(std::string("Failed to received message ") + pulsar_result_str(result));
+    }
+  }
+  void OnOK() {
+    Napi::Object obj = Message::NewInstance({}, this->cMessage);
+    this->deferred.Resolve(obj);
+  }
+  void OnError(const Napi::Error &e) { this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value()); }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_reader_t *cReader;
+  pulsar_message_t *cMessage;
+  int64_t timeout;
+};
+
+Napi::Value Reader::ReadNext(const Napi::CallbackInfo &info) {
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  if (info[0].IsUndefined()) {
+    ReaderReadNextWorker *wk = new ReaderReadNextWorker(deferred, this->cReader);
+    wk->Queue();
+  } else {
+    Napi::Number timeout = info[0].As<Napi::Object>().ToNumber();
+    ReaderReadNextWorker *wk = new ReaderReadNextWorker(deferred, this->cReader, timeout.Int64Value());
+    wk->Queue();
+  }
+  return deferred.Promise();
+}
+
+Napi::Value Reader::HasNext(const Napi::CallbackInfo &info) {
+  int value = 0;
+  pulsar_result result = pulsar_reader_has_message_available(this->cReader, &value);
+  if (result != pulsar_result_Ok || value != 1) {
+    return Napi::Boolean::New(info.Env(), false);
+  } else {
+    return Napi::Boolean::New(info.Env(), true);
+  }
+}
+
+class ReaderCloseWorker : public Napi::AsyncWorker {
+ public:
+  ReaderCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_reader_t *cReader)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cReader(cReader) {}
+  ~ReaderCloseWorker() {}
+  void Execute() {
+    pulsar_result result = pulsar_reader_close(this->cReader);
+    if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+  }
+  void OnOK() { this->deferred.Resolve(Env().Null()); }
+  void OnError(const Napi::Error &e) {
+    this->deferred.Reject(
+        Napi::Error::New(Env(), std::string("Failed to close reader: ") + e.Message()).Value());
+  }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_reader_t *cReader;
+};
+
+Napi::Value Reader::Close(const Napi::CallbackInfo &info) {
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  ReaderCloseWorker *wk = new ReaderCloseWorker(deferred, this->cReader);
+  wk->Queue();
+  return deferred.Promise();
+}
+
+Reader::~Reader() { pulsar_reader_free(this->cReader); }
diff --git a/src/Client.h b/src/Reader.h
similarity index 67%
copy from src/Client.h
copy to src/Reader.h
index 63c66f2..787b732 100644
--- a/src/Client.h
+++ b/src/Reader.h
@@ -17,24 +17,26 @@
  * under the License.
  */
 
-#ifndef CLIENT_H
-#define CLIENT_H
+#ifndef READER_H
+#define READER_H
 
 #include <napi.h>
 #include <pulsar/c/client.h>
 
-class Client : public Napi::ObjectWrap<Client> {
+class Reader : public Napi::ObjectWrap<Reader> {
  public:
-  static Napi::Object Init(Napi::Env env, Napi::Object exports);
-  Client(const Napi::CallbackInfo &info);
-  ~Client();
+  static void Init(Napi::Env env, Napi::Object exports);
+  static Napi::Value NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient);
+  static Napi::FunctionReference constructor;
+  Reader(const Napi::CallbackInfo &info);
+  ~Reader();
+  void SetCReader(pulsar_reader_t *cReader);
 
  private:
-  static Napi::FunctionReference constructor;
-  pulsar_client_t *cClient;
+  pulsar_reader_t *cReader;
 
-  Napi::Value CreateProducer(const Napi::CallbackInfo &info);
-  Napi::Value Subscribe(const Napi::CallbackInfo &info);
+  Napi::Value ReadNext(const Napi::CallbackInfo &info);
+  Napi::Value HasNext(const Napi::CallbackInfo &info);
   Napi::Value Close(const Napi::CallbackInfo &info);
 };
 
diff --git a/src/ReaderConfig.cc b/src/ReaderConfig.cc
new file mode 100644
index 0000000..7e15807
--- /dev/null
+++ b/src/ReaderConfig.cc
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "ReaderConfig.h"
+#include "MessageId.h"
+#include <map>
+
+static const std::string CFG_TOPIC = "topic";
+static const std::string CFG_START_MESSAGE_ID = "startMessageId";
+static const std::string CFG_RECV_QUEUE = "receiverQueueSize";
+static const std::string CFG_READER_NAME = "readerName";
+static const std::string CFG_SUBSCRIPTION_ROLE_PREFIX = "subscriptionRolePrefix";
+
+ReaderConfig::ReaderConfig(const Napi::Object &readerConfig) : topic(""), cStartMessageId(NULL) {
+  this->cReaderConfig = pulsar_reader_configuration_create();
+
+  if (readerConfig.Has(CFG_TOPIC) && readerConfig.Get(CFG_TOPIC).IsString()) {
+    this->topic = readerConfig.Get(CFG_TOPIC).ToString().Utf8Value();
+  }
+  if (readerConfig.Has(CFG_START_MESSAGE_ID) && readerConfig.Get(CFG_START_MESSAGE_ID).IsObject()) {
+    Napi::Object objMessageId = readerConfig.Get(CFG_START_MESSAGE_ID).ToObject();
+    MessageId *msgId = MessageId::Unwrap(objMessageId);
+    this->cStartMessageId = msgId->GetCMessageId();
+  }
+
+  if (readerConfig.Has(CFG_RECV_QUEUE) && readerConfig.Get(CFG_RECV_QUEUE).IsNumber()) {
+    int32_t receiverQueueSize = readerConfig.Get(CFG_RECV_QUEUE).ToNumber().Int32Value();
+    if (receiverQueueSize >= 0) {
+      pulsar_reader_configuration_set_receiver_queue_size(this->cReaderConfig, receiverQueueSize);
+    }
+  }
+
+  if (readerConfig.Has(CFG_READER_NAME) && readerConfig.Get(CFG_READER_NAME).IsString()) {
+    std::string readerName = readerConfig.Get(CFG_READER_NAME).ToString().Utf8Value();
+    if (!readerName.empty())
+      pulsar_reader_configuration_set_reader_name(this->cReaderConfig, readerName.c_str());
+  }
+
+  if (readerConfig.Has(CFG_SUBSCRIPTION_ROLE_PREFIX) &&
+      readerConfig.Get(CFG_SUBSCRIPTION_ROLE_PREFIX).IsString()) {
+    std::string subscriptionRolePrefix =
+        readerConfig.Get(CFG_SUBSCRIPTION_ROLE_PREFIX).ToString().Utf8Value();
+    if (!subscriptionRolePrefix.empty())
+      pulsar_reader_configuration_set_reader_name(this->cReaderConfig, subscriptionRolePrefix.c_str());
+  }
+}
+
+ReaderConfig::~ReaderConfig() { pulsar_reader_configuration_free(this->cReaderConfig); }
+
+pulsar_reader_configuration_t *ReaderConfig::GetCReaderConfig() { return this->cReaderConfig; }
+
+std::string ReaderConfig::GetTopic() { return this->topic; }
+
+pulsar_message_id_t *ReaderConfig::GetCStartMessageId() { return this->cStartMessageId; }
diff --git a/src/Client.h b/src/ReaderConfig.h
similarity index 64%
copy from src/Client.h
copy to src/ReaderConfig.h
index 63c66f2..1983459 100644
--- a/src/Client.h
+++ b/src/ReaderConfig.h
@@ -17,25 +17,26 @@
  * under the License.
  */
 
-#ifndef CLIENT_H
-#define CLIENT_H
+#ifndef READER_CONFIG_H
+#define READER_CONFIG_H
 
 #include <napi.h>
-#include <pulsar/c/client.h>
+#include <pulsar/c/reader.h>
+#include <pulsar/c/reader_configuration.h>
+#include <pulsar/c/message_id.h>
 
-class Client : public Napi::ObjectWrap<Client> {
+class ReaderConfig {
  public:
-  static Napi::Object Init(Napi::Env env, Napi::Object exports);
-  Client(const Napi::CallbackInfo &info);
-  ~Client();
+  ReaderConfig(const Napi::Object &readerConfig);
+  ~ReaderConfig();
+  pulsar_reader_configuration_t *GetCReaderConfig();
+  pulsar_message_id_t *GetCStartMessageId();
+  std::string GetTopic();
 
  private:
-  static Napi::FunctionReference constructor;
-  pulsar_client_t *cClient;
-
-  Napi::Value CreateProducer(const Napi::CallbackInfo &info);
-  Napi::Value Subscribe(const Napi::CallbackInfo &info);
-  Napi::Value Close(const Napi::CallbackInfo &info);
+  pulsar_reader_configuration_t *cReaderConfig;
+  pulsar_message_id_t *cStartMessageId;
+  std::string topic;
 };
 
 #endif
diff --git a/src/addon.cc b/src/addon.cc
index 050ae12..fa26ae0 100644
--- a/src/addon.cc
+++ b/src/addon.cc
@@ -23,6 +23,7 @@
 #include "Producer.h"
 #include "Consumer.h"
 #include "Client.h"
+#include "Reader.h"
 #include <napi.h>
 
 Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
@@ -31,6 +32,7 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
   Authentication::Init(env, exports);
   Producer::Init(env, exports);
   Consumer::Init(env, exports);
+  Reader::Init(env, exports);
   return Client::Init(env, exports);
 }
 
diff --git a/tests/end_to_end.test.js b/tests/end_to_end.test.js
index ae24d86..9f15cb7 100644
--- a/tests/end_to_end.test.js
+++ b/tests/end_to_end.test.js
@@ -23,11 +23,12 @@ const Pulsar = require('../index.js');
 
 (() => {
   describe('End To End', () => {
-    const client = new Pulsar.Client({
-      serviceUrl: 'pulsar://localhost:6650',
-      operationTimeoutSeconds: 30,
-    });
     test('Produce/Consume', async () => {
+      const client = new Pulsar.Client({
+        serviceUrl: 'pulsar://localhost:6650',
+        operationTimeoutSeconds: 30,
+      });
+
       const producer = await client.createProducer({
         topic: 'persistent://public/default/test-end-to-end',
         sendTimeoutMs: 30000,
@@ -63,9 +64,15 @@ const Pulsar = require('../index.js');
 
       await producer.close();
       await consumer.close();
+      await client.close();
     });
 
     test('acknowledgeCumulative', async () => {
+      const client = new Pulsar.Client({
+        serviceUrl: 'pulsar://localhost:6650',
+        operationTimeoutSeconds: 30,
+      });
+
       const producer = await client.createProducer({
         topic: 'persistent://public/default/acknowledgeCumulative',
         sendTimeoutMs: 30000,
@@ -103,5 +110,51 @@ const Pulsar = require('../index.js');
       await consumer.close();
       await client.close();
     });
+
+    test('Produce/Read', async () => {
+      const client = new Pulsar.Client({
+        serviceUrl: 'pulsar://localhost:6650',
+        operationTimeoutSeconds: 30,
+      });
+      expect(client).not.toBeNull();
+
+      const producer = await client.createProducer({
+        topic: 'persistent://public/default/test-end-to-end',
+        sendTimeoutMs: 30000,
+        batchingEnabled: true,
+      });
+      expect(producer).not.toBeNull();
+
+      const reader = await client.createReader({
+        topic: 'persistent://public/default/test-end-to-end',
+        startMessageId: Pulsar.MessageId.latest(),
+      });
+      expect(reader).not.toBeNull();
+
+      const messages = [];
+      for (let i = 0; i < 10; i += 1) {
+        const msg = `my-message-${i}`;
+        producer.send({
+          data: Buffer.from(msg),
+        });
+        messages.push(msg);
+      }
+      await producer.flush();
+
+      expect(reader.hasNext()).toBe(true);
+
+      const results = [];
+      for (let i = 0; i < 10; i += 1) {
+        const msg = await reader.readNext();
+        results.push(msg.getData().toString());
+      }
+      expect(lodash.difference(messages, results)).toEqual([]);
+
+      expect(reader.hasNext()).toBe(false);
+
+      await producer.close();
+      await reader.close();
+      await client.close();
+    });
   });
 })();
diff --git a/tests/reader.test.js b/tests/reader.test.js
new file mode 100644
index 0000000..9c87212
--- /dev/null
+++ b/tests/reader.test.js
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const Pulsar = require('../index.js');
+
+(() => {
+  describe('Reader', () => {
+    describe('Create', () => {
+      test('No Topic', async () => {
+        const client = new Pulsar.Client({
+          serviceUrl: 'pulsar://localhost:6650',
+          operationTimeoutSeconds: 30,
+        });
+        await expect(client.createReader({
+          startMessageId: Pulsar.MessageId.earliest(),
+        })).rejects.toThrow('Topic is required and must be specified as a string when creating reader');
+        await client.close();
+      });
+
+      test('No Topic', async () => {
+        const client = new Pulsar.Client({
+          serviceUrl: 'pulsar://localhost:6650',
+          operationTimeoutSeconds: 30,
+        });
+        await expect(client.createReader({
+          topic: 0,
+          startMessageId: Pulsar.MessageId.earliest(),
+        })).rejects.toThrow('Topic is required and must be specified as a string when creating reader');
+        await client.close();
+      });
+
+      test('No StartMessageId', async () => {
+        const client = new Pulsar.Client({
+          serviceUrl: 'pulsar://localhost:6650',
+          operationTimeoutSeconds: 30,
+        });
+        await expect(client.createReader({
+          topic: 'persistent://public/default/topic',
+        })).rejects.toThrow('StartMessageId is required and must be specified as a MessageId object when creating reader');
+        await client.close();
+      });
+
+      test('Not StartMessageId as MessageId', async () => {
+        const client = new Pulsar.Client({
+          serviceUrl: 'pulsar://localhost:6650',
+          operationTimeoutSeconds: 30,
+        });
+        await expect(client.createReader({
+          topic: 'persistent://public/default/topic',
+          startMessageId: 'not MessageId',
+        })).rejects.toThrow('StartMessageId is required and must be specified as a MessageId object when creating reader');
+        await client.close();
+      });
+    });
+  });
+})();


[pulsar-client-node] 03/36: change 4 to 2 spaces and reformat

Posted by nk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nkurihar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git

commit 196a69c30e238e1438f4964b6643bfc4b56ed903
Author: yfuruta <yf...@yahoo-corp.jp>
AuthorDate: Tue Mar 12 10:48:30 2019 +0900

    change 4 to 2 spaces and reformat
---
 .clang-format         |   2 +-
 src/Client.cc         | 194 +++++++++++++++++++-------------------
 src/Client.h          |  20 ++--
 src/Consumer.cc       | 243 ++++++++++++++++++++++++------------------------
 src/Consumer.h        |  26 +++---
 src/ConsumerConfig.cc |  89 +++++++++---------
 src/ConsumerConfig.h  |  24 ++---
 src/Message.cc        | 252 +++++++++++++++++++++++++-------------------------
 src/Message.h         |  60 ++++++------
 src/MessageId.cc      |  64 ++++++-------
 src/MessageId.h       |  28 +++---
 src/Producer.cc       | 194 +++++++++++++++++++-------------------
 src/Producer.h        |  22 ++---
 src/ProducerConfig.cc | 181 ++++++++++++++++++------------------
 src/ProducerConfig.h  |  16 ++--
 src/addon.cc          |  10 +-
 16 files changed, 707 insertions(+), 718 deletions(-)

diff --git a/.clang-format b/.clang-format
index cb40b50..f2d174b 100644
--- a/.clang-format
+++ b/.clang-format
@@ -17,7 +17,7 @@
 
 
 BasedOnStyle: Google
-IndentWidth: 4
+IndentWidth: 2
 ColumnLimit: 110
 SortIncludes: false
 BreakBeforeBraces: Custom
diff --git a/src/Client.cc b/src/Client.cc
index 97fc573..16fb13d 100644
--- a/src/Client.cc
+++ b/src/Client.cc
@@ -38,133 +38,133 @@ static const std::string CFG_STATS_INTERVAL = "statsIntervalInSeconds";
 Napi::FunctionReference Client::constructor;
 
 Napi::Object Client::Init(Napi::Env env, Napi::Object exports) {
-    Napi::HandleScope scope(env);
+  Napi::HandleScope scope(env);
 
-    Napi::Function func = DefineClass(
-        env, "Client",
-        {InstanceMethod("createProducer", &Client::CreateProducer),
-         InstanceMethod("subscribe", &Client::Subscribe), InstanceMethod("close", &Client::Close)});
+  Napi::Function func =
+      DefineClass(env, "Client",
+                  {InstanceMethod("createProducer", &Client::CreateProducer),
+                   InstanceMethod("subscribe", &Client::Subscribe), InstanceMethod("close", &Client::Close)});
 
-    constructor = Napi::Persistent(func);
-    constructor.SuppressDestruct();
+  constructor = Napi::Persistent(func);
+  constructor.SuppressDestruct();
 
-    exports.Set("Client", func);
-    return exports;
+  exports.Set("Client", func);
+  return exports;
 }
 
 Client::Client(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Client>(info) {
-    Napi::Env env = info.Env();
-    Napi::HandleScope scope(env);
-    Napi::Object clientConfig = info[0].As<Napi::Object>();
-
-    if (!clientConfig.Has(CFG_SERVICE_URL) || !clientConfig.Get(CFG_SERVICE_URL).IsString()) {
-        if (clientConfig.Get(CFG_SERVICE_URL).ToString().Utf8Value().empty()) {
-            Napi::Error::New(env, "Service URL is required and must be specified as a string")
-                .ThrowAsJavaScriptException();
-            return;
-        }
+  Napi::Env env = info.Env();
+  Napi::HandleScope scope(env);
+  Napi::Object clientConfig = info[0].As<Napi::Object>();
+
+  if (!clientConfig.Has(CFG_SERVICE_URL) || !clientConfig.Get(CFG_SERVICE_URL).IsString()) {
+    if (clientConfig.Get(CFG_SERVICE_URL).ToString().Utf8Value().empty()) {
+      Napi::Error::New(env, "Service URL is required and must be specified as a string")
+          .ThrowAsJavaScriptException();
+      return;
     }
-    Napi::String serviceUrl = clientConfig.Get(CFG_SERVICE_URL).ToString();
+  }
+  Napi::String serviceUrl = clientConfig.Get(CFG_SERVICE_URL).ToString();
 
-    pulsar_client_configuration_t *cClientConfig = pulsar_client_configuration_create();
+  pulsar_client_configuration_t *cClientConfig = pulsar_client_configuration_create();
 
-    if (clientConfig.Has(CFG_OP_TIMEOUT) && clientConfig.Get(CFG_OP_TIMEOUT).IsNumber()) {
-        int32_t operationTimeoutSeconds = clientConfig.Get(CFG_OP_TIMEOUT).ToNumber().Int32Value();
-        if (operationTimeoutSeconds > 0) {
-            pulsar_client_configuration_set_operation_timeout_seconds(cClientConfig, operationTimeoutSeconds);
-        }
+  if (clientConfig.Has(CFG_OP_TIMEOUT) && clientConfig.Get(CFG_OP_TIMEOUT).IsNumber()) {
+    int32_t operationTimeoutSeconds = clientConfig.Get(CFG_OP_TIMEOUT).ToNumber().Int32Value();
+    if (operationTimeoutSeconds > 0) {
+      pulsar_client_configuration_set_operation_timeout_seconds(cClientConfig, operationTimeoutSeconds);
     }
+  }
 
-    if (clientConfig.Has(CFG_IO_THREADS) && clientConfig.Get(CFG_IO_THREADS).IsNumber()) {
-        int32_t ioThreads = clientConfig.Get(CFG_IO_THREADS).ToNumber().Int32Value();
-        if (ioThreads > 0) {
-            pulsar_client_configuration_set_io_threads(cClientConfig, ioThreads);
-        }
+  if (clientConfig.Has(CFG_IO_THREADS) && clientConfig.Get(CFG_IO_THREADS).IsNumber()) {
+    int32_t ioThreads = clientConfig.Get(CFG_IO_THREADS).ToNumber().Int32Value();
+    if (ioThreads > 0) {
+      pulsar_client_configuration_set_io_threads(cClientConfig, ioThreads);
     }
+  }
 
-    if (clientConfig.Has(CFG_LISTENER_THREADS) && clientConfig.Get(CFG_LISTENER_THREADS).IsNumber()) {
-        int32_t messageListenerThreads = clientConfig.Get(CFG_LISTENER_THREADS).ToNumber().Int32Value();
-        if (messageListenerThreads > 0) {
-            pulsar_client_configuration_set_message_listener_threads(cClientConfig, messageListenerThreads);
-        }
+  if (clientConfig.Has(CFG_LISTENER_THREADS) && clientConfig.Get(CFG_LISTENER_THREADS).IsNumber()) {
+    int32_t messageListenerThreads = clientConfig.Get(CFG_LISTENER_THREADS).ToNumber().Int32Value();
+    if (messageListenerThreads > 0) {
+      pulsar_client_configuration_set_message_listener_threads(cClientConfig, messageListenerThreads);
     }
+  }
 
-    if (clientConfig.Has(CFG_CONCURRENT_LOOKUP) && clientConfig.Get(CFG_CONCURRENT_LOOKUP).IsNumber()) {
-        int32_t concurrentLookupRequest = clientConfig.Get(CFG_CONCURRENT_LOOKUP).ToNumber().Int32Value();
-        if (concurrentLookupRequest > 0) {
-            pulsar_client_configuration_set_concurrent_lookup_request(cClientConfig, concurrentLookupRequest);
-        }
+  if (clientConfig.Has(CFG_CONCURRENT_LOOKUP) && clientConfig.Get(CFG_CONCURRENT_LOOKUP).IsNumber()) {
+    int32_t concurrentLookupRequest = clientConfig.Get(CFG_CONCURRENT_LOOKUP).ToNumber().Int32Value();
+    if (concurrentLookupRequest > 0) {
+      pulsar_client_configuration_set_concurrent_lookup_request(cClientConfig, concurrentLookupRequest);
     }
-
-    if (clientConfig.Has(CFG_USE_TLS) && clientConfig.Get(CFG_USE_TLS).IsBoolean()) {
-        Napi::Boolean useTls = clientConfig.Get(CFG_USE_TLS).ToBoolean();
-        pulsar_client_configuration_set_use_tls(cClientConfig, useTls.Value());
-    }
-
-    if (clientConfig.Has(CFG_TLS_TRUST_CERT) && clientConfig.Get(CFG_TLS_TRUST_CERT).IsString()) {
-        Napi::String tlsTrustCertsFilePath = clientConfig.Get(CFG_TLS_TRUST_CERT).ToString();
-        pulsar_client_configuration_set_tls_trust_certs_file_path(cClientConfig,
-                                                                  tlsTrustCertsFilePath.Utf8Value().c_str());
-    }
-
-    if (clientConfig.Has(CFG_TLS_VALIDATE_HOSTNAME) &&
-        clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).IsBoolean()) {
-        Napi::Boolean tlsValidateHostname = clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).ToBoolean();
-        pulsar_client_configuration_set_validate_hostname(cClientConfig, tlsValidateHostname.Value());
-    }
-
-    if (clientConfig.Has(CFG_TLS_ALLOW_INSECURE) && clientConfig.Get(CFG_TLS_ALLOW_INSECURE).IsBoolean()) {
-        Napi::Boolean tlsAllowInsecureConnection = clientConfig.Get(CFG_TLS_ALLOW_INSECURE).ToBoolean();
-        pulsar_client_configuration_set_tls_allow_insecure_connection(cClientConfig,
-                                                                      tlsAllowInsecureConnection.Value());
-    }
-
-    if (clientConfig.Has(CFG_STATS_INTERVAL) && clientConfig.Get(CFG_STATS_INTERVAL).IsNumber()) {
-        uint32_t statsIntervalInSeconds = clientConfig.Get(CFG_STATS_INTERVAL).ToNumber().Uint32Value();
-        if (statsIntervalInSeconds > 0) {
-            pulsar_client_configuration_set_stats_interval_in_seconds(cClientConfig, statsIntervalInSeconds);
-        }
+  }
+
+  if (clientConfig.Has(CFG_USE_TLS) && clientConfig.Get(CFG_USE_TLS).IsBoolean()) {
+    Napi::Boolean useTls = clientConfig.Get(CFG_USE_TLS).ToBoolean();
+    pulsar_client_configuration_set_use_tls(cClientConfig, useTls.Value());
+  }
+
+  if (clientConfig.Has(CFG_TLS_TRUST_CERT) && clientConfig.Get(CFG_TLS_TRUST_CERT).IsString()) {
+    Napi::String tlsTrustCertsFilePath = clientConfig.Get(CFG_TLS_TRUST_CERT).ToString();
+    pulsar_client_configuration_set_tls_trust_certs_file_path(cClientConfig,
+                                                              tlsTrustCertsFilePath.Utf8Value().c_str());
+  }
+
+  if (clientConfig.Has(CFG_TLS_VALIDATE_HOSTNAME) &&
+      clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).IsBoolean()) {
+    Napi::Boolean tlsValidateHostname = clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).ToBoolean();
+    pulsar_client_configuration_set_validate_hostname(cClientConfig, tlsValidateHostname.Value());
+  }
+
+  if (clientConfig.Has(CFG_TLS_ALLOW_INSECURE) && clientConfig.Get(CFG_TLS_ALLOW_INSECURE).IsBoolean()) {
+    Napi::Boolean tlsAllowInsecureConnection = clientConfig.Get(CFG_TLS_ALLOW_INSECURE).ToBoolean();
+    pulsar_client_configuration_set_tls_allow_insecure_connection(cClientConfig,
+                                                                  tlsAllowInsecureConnection.Value());
+  }
+
+  if (clientConfig.Has(CFG_STATS_INTERVAL) && clientConfig.Get(CFG_STATS_INTERVAL).IsNumber()) {
+    uint32_t statsIntervalInSeconds = clientConfig.Get(CFG_STATS_INTERVAL).ToNumber().Uint32Value();
+    if (statsIntervalInSeconds > 0) {
+      pulsar_client_configuration_set_stats_interval_in_seconds(cClientConfig, statsIntervalInSeconds);
     }
+  }
 
-    this->cClient = pulsar_client_create(serviceUrl.Utf8Value().c_str(), cClientConfig);
-    pulsar_client_configuration_free(cClientConfig);
+  this->cClient = pulsar_client_create(serviceUrl.Utf8Value().c_str(), cClientConfig);
+  pulsar_client_configuration_free(cClientConfig);
 }
 
 Client::~Client() { pulsar_client_free(this->cClient); }
 
 Napi::Value Client::CreateProducer(const Napi::CallbackInfo &info) {
-    return Producer::NewInstance(info, this->cClient);
+  return Producer::NewInstance(info, this->cClient);
 }
 
 Napi::Value Client::Subscribe(const Napi::CallbackInfo &info) {
-    return Consumer::NewInstance(info, this->cClient);
+  return Consumer::NewInstance(info, this->cClient);
 }
 
 class ClientCloseWorker : public Napi::AsyncWorker {
-   public:
-    ClientCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient)
-        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
-          deferred(deferred),
-          cClient(cClient) {}
-    ~ClientCloseWorker() {}
-    void Execute() {
-        pulsar_result result = pulsar_client_close(this->cClient);
-        if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
-    }
-    void OnOK() { this->deferred.Resolve(Env().Null()); }
-    void OnError(const Napi::Error &e) {
-        this->deferred.Reject(
-            Napi::Error::New(Env(), std::string("Failed to close client: ") + e.Message()).Value());
-    }
-
-   private:
-    Napi::Promise::Deferred deferred;
-    pulsar_client_t *cClient;
+ public:
+  ClientCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cClient(cClient) {}
+  ~ClientCloseWorker() {}
+  void Execute() {
+    pulsar_result result = pulsar_client_close(this->cClient);
+    if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+  }
+  void OnOK() { this->deferred.Resolve(Env().Null()); }
+  void OnError(const Napi::Error &e) {
+    this->deferred.Reject(
+        Napi::Error::New(Env(), std::string("Failed to close client: ") + e.Message()).Value());
+  }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_client_t *cClient;
 };
 
 Napi::Value Client::Close(const Napi::CallbackInfo &info) {
-    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
-    ClientCloseWorker *wk = new ClientCloseWorker(deferred, this->cClient);
-    wk->Queue();
-    return deferred.Promise();
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  ClientCloseWorker *wk = new ClientCloseWorker(deferred, this->cClient);
+  wk->Queue();
+  return deferred.Promise();
 }
diff --git a/src/Client.h b/src/Client.h
index bc92125..63c66f2 100644
--- a/src/Client.h
+++ b/src/Client.h
@@ -24,18 +24,18 @@
 #include <pulsar/c/client.h>
 
 class Client : public Napi::ObjectWrap<Client> {
-   public:
-    static Napi::Object Init(Napi::Env env, Napi::Object exports);
-    Client(const Napi::CallbackInfo &info);
-    ~Client();
+ public:
+  static Napi::Object Init(Napi::Env env, Napi::Object exports);
+  Client(const Napi::CallbackInfo &info);
+  ~Client();
 
-   private:
-    static Napi::FunctionReference constructor;
-    pulsar_client_t *cClient;
+ private:
+  static Napi::FunctionReference constructor;
+  pulsar_client_t *cClient;
 
-    Napi::Value CreateProducer(const Napi::CallbackInfo &info);
-    Napi::Value Subscribe(const Napi::CallbackInfo &info);
-    Napi::Value Close(const Napi::CallbackInfo &info);
+  Napi::Value CreateProducer(const Napi::CallbackInfo &info);
+  Napi::Value Subscribe(const Napi::CallbackInfo &info);
+  Napi::Value Close(const Napi::CallbackInfo &info);
 };
 
 #endif
diff --git a/src/Consumer.cc b/src/Consumer.cc
index 85e0fbf..db6fab4 100644
--- a/src/Consumer.cc
+++ b/src/Consumer.cc
@@ -26,18 +26,18 @@
 Napi::FunctionReference Consumer::constructor;
 
 void Consumer::Init(Napi::Env env, Napi::Object exports) {
-    Napi::HandleScope scope(env);
-
-    Napi::Function func = DefineClass(env, "Consumer",
-                                      {
-                                          InstanceMethod("receive", &Consumer::Receive),
-                                          InstanceMethod("acknowledge", &Consumer::Acknowledge),
-                                          InstanceMethod("acknowledgeId", &Consumer::AcknowledgeId),
-                                          InstanceMethod("close", &Consumer::Close),
-                                      });
-
-    constructor = Napi::Persistent(func);
-    constructor.SuppressDestruct();
+  Napi::HandleScope scope(env);
+
+  Napi::Function func = DefineClass(env, "Consumer",
+                                    {
+                                        InstanceMethod("receive", &Consumer::Receive),
+                                        InstanceMethod("acknowledge", &Consumer::Acknowledge),
+                                        InstanceMethod("acknowledgeId", &Consumer::AcknowledgeId),
+                                        InstanceMethod("close", &Consumer::Close),
+                                    });
+
+  constructor = Napi::Persistent(func);
+  constructor.SuppressDestruct();
 }
 
 void Consumer::SetCConsumer(pulsar_consumer_t *cConsumer) { this->cConsumer = cConsumer; }
@@ -45,144 +45,139 @@ void Consumer::SetCConsumer(pulsar_consumer_t *cConsumer) { this->cConsumer = cC
 Consumer::Consumer(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Consumer>(info) {}
 
 class ConsumerNewInstanceWorker : public Napi::AsyncWorker {
-   public:
-    ConsumerNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
-                              ConsumerConfig *consumerConfig)
-        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
-          deferred(deferred),
-          cClient(cClient),
-          consumerConfig(consumerConfig) {}
-    ~ConsumerNewInstanceWorker() {}
-    void Execute() {
-        const std::string &topic = this->consumerConfig->GetTopic();
-        if (topic.empty()) {
-            SetError(
-                std::string("Topic is required and must be specified as a string when creating consumer"));
-            return;
-        }
-        const std::string &subscription = this->consumerConfig->GetSubscription();
-        if (subscription.empty()) {
-            SetError(std::string(
-                "Subscription is required and must be specified as a string when creating consumer"));
-            return;
-        }
-        int32_t ackTimeoutMs = this->consumerConfig->GetAckTimeoutMs();
-        if (ackTimeoutMs != 0 && ackTimeoutMs < MIN_ACK_TIMEOUT_MILLIS) {
-            std::string msg("Ack timeout should be 0 or greater than or equal to " +
-                            std::to_string(MIN_ACK_TIMEOUT_MILLIS));
-            SetError(msg);
-            return;
-        }
-
-        pulsar_result result =
-            pulsar_client_subscribe(this->cClient, topic.c_str(), subscription.c_str(),
-                                    this->consumerConfig->GetCConsumerConfig(), &(this->cConsumer));
-        delete this->consumerConfig;
-        if (result != pulsar_result_Ok) {
-            SetError(std::string("Failed to create consumer: ") + pulsar_result_str(result));
-            return;
-        }
+ public:
+  ConsumerNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
+                            ConsumerConfig *consumerConfig)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cClient(cClient),
+        consumerConfig(consumerConfig) {}
+  ~ConsumerNewInstanceWorker() {}
+  void Execute() {
+    const std::string &topic = this->consumerConfig->GetTopic();
+    if (topic.empty()) {
+      SetError(std::string("Topic is required and must be specified as a string when creating consumer"));
+      return;
     }
-    void OnOK() {
-        Napi::Object obj = Consumer::constructor.New({});
-        Consumer *consumer = Consumer::Unwrap(obj);
-        consumer->SetCConsumer(this->cConsumer);
-        this->deferred.Resolve(obj);
+    const std::string &subscription = this->consumerConfig->GetSubscription();
+    if (subscription.empty()) {
+      SetError(
+          std::string("Subscription is required and must be specified as a string when creating consumer"));
+      return;
     }
-    void OnError(const Napi::Error &e) {
-        this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value());
+    int32_t ackTimeoutMs = this->consumerConfig->GetAckTimeoutMs();
+    if (ackTimeoutMs != 0 && ackTimeoutMs < MIN_ACK_TIMEOUT_MILLIS) {
+      std::string msg("Ack timeout should be 0 or greater than or equal to " +
+                      std::to_string(MIN_ACK_TIMEOUT_MILLIS));
+      SetError(msg);
+      return;
     }
 
-   private:
-    Napi::Promise::Deferred deferred;
-    pulsar_client_t *cClient;
-    ConsumerConfig *consumerConfig;
-    pulsar_consumer_t *cConsumer;
+    pulsar_result result =
+        pulsar_client_subscribe(this->cClient, topic.c_str(), subscription.c_str(),
+                                this->consumerConfig->GetCConsumerConfig(), &(this->cConsumer));
+    delete this->consumerConfig;
+    if (result != pulsar_result_Ok) {
+      SetError(std::string("Failed to create consumer: ") + pulsar_result_str(result));
+      return;
+    }
+  }
+  void OnOK() {
+    Napi::Object obj = Consumer::constructor.New({});
+    Consumer *consumer = Consumer::Unwrap(obj);
+    consumer->SetCConsumer(this->cConsumer);
+    this->deferred.Resolve(obj);
+  }
+  void OnError(const Napi::Error &e) { this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value()); }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_client_t *cClient;
+  ConsumerConfig *consumerConfig;
+  pulsar_consumer_t *cConsumer;
 };
 
 Napi::Value Consumer::NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient) {
-    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
-    Napi::Object config = info[0].As<Napi::Object>();
-    ConsumerConfig *consumerConfig = new ConsumerConfig(config);
-    ConsumerNewInstanceWorker *wk = new ConsumerNewInstanceWorker(deferred, cClient, consumerConfig);
-    wk->Queue();
-    return deferred.Promise();
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  Napi::Object config = info[0].As<Napi::Object>();
+  ConsumerConfig *consumerConfig = new ConsumerConfig(config);
+  ConsumerNewInstanceWorker *wk = new ConsumerNewInstanceWorker(deferred, cClient, consumerConfig);
+  wk->Queue();
+  return deferred.Promise();
 }
 
 class ConsumerReceiveWorker : public Napi::AsyncWorker {
-   public:
-    ConsumerReceiveWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer)
-        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
-          deferred(deferred),
-          cConsumer(cConsumer) {}
-    ~ConsumerReceiveWorker() {}
-    void Execute() {
-        pulsar_result result = pulsar_consumer_receive(this->cConsumer, &(this->cMessage));
-
-        if (result != pulsar_result_Ok) {
-            SetError(std::string("Failed to received message ") + pulsar_result_str(result));
-        }
-    }
-    void OnOK() {
-        Napi::Object obj = Message::NewInstance({}, this->cMessage);
-        this->deferred.Resolve(obj);
+ public:
+  ConsumerReceiveWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cConsumer(cConsumer) {}
+  ~ConsumerReceiveWorker() {}
+  void Execute() {
+    pulsar_result result = pulsar_consumer_receive(this->cConsumer, &(this->cMessage));
+
+    if (result != pulsar_result_Ok) {
+      SetError(std::string("Failed to received message ") + pulsar_result_str(result));
     }
-    void OnError(const Napi::Error &e) {
-        this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value());
-    }
-
-   private:
-    Napi::Promise::Deferred deferred;
-    pulsar_consumer_t *cConsumer;
-    pulsar_message_t *cMessage;
+  }
+  void OnOK() {
+    Napi::Object obj = Message::NewInstance({}, this->cMessage);
+    this->deferred.Resolve(obj);
+  }
+  void OnError(const Napi::Error &e) { this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value()); }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_consumer_t *cConsumer;
+  pulsar_message_t *cMessage;
 };
 
 Napi::Value Consumer::Receive(const Napi::CallbackInfo &info) {
-    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
-    ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->cConsumer);
-    wk->Queue();
-    return deferred.Promise();
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  ConsumerReceiveWorker *wk = new ConsumerReceiveWorker(deferred, this->cConsumer);
+  wk->Queue();
+  return deferred.Promise();
 }
 
 void Consumer::Acknowledge(const Napi::CallbackInfo &info) {
-    Napi::Object obj = info[0].As<Napi::Object>();
-    Message *msg = Message::Unwrap(obj);
-    pulsar_consumer_acknowledge_async(this->cConsumer, msg->GetCMessage(), NULL, NULL);
+  Napi::Object obj = info[0].As<Napi::Object>();
+  Message *msg = Message::Unwrap(obj);
+  pulsar_consumer_acknowledge_async(this->cConsumer, msg->GetCMessage(), NULL, NULL);
 }
 
 void Consumer::AcknowledgeId(const Napi::CallbackInfo &info) {
-    Napi::Object obj = info[0].As<Napi::Object>();
-    MessageId *msgId = MessageId::Unwrap(obj);
-    pulsar_consumer_acknowledge_async_id(this->cConsumer, msgId->GetCMessageId(), NULL, NULL);
+  Napi::Object obj = info[0].As<Napi::Object>();
+  MessageId *msgId = MessageId::Unwrap(obj);
+  pulsar_consumer_acknowledge_async_id(this->cConsumer, msgId->GetCMessageId(), NULL, NULL);
 }
 
 class ConsumerCloseWorker : public Napi::AsyncWorker {
-   public:
-    ConsumerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer)
-        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
-          deferred(deferred),
-          cConsumer(cConsumer) {}
-    ~ConsumerCloseWorker() {}
-    void Execute() {
-        pulsar_result result = pulsar_consumer_close(this->cConsumer);
-        if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
-    }
-    void OnOK() { this->deferred.Resolve(Env().Null()); }
-    void OnError(const Napi::Error &e) {
-        this->deferred.Reject(
-            Napi::Error::New(Env(), std::string("Failed to close consumer: ") + e.Message()).Value());
-    }
-
-   private:
-    Napi::Promise::Deferred deferred;
-    pulsar_consumer_t *cConsumer;
+ public:
+  ConsumerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_consumer_t *cConsumer)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cConsumer(cConsumer) {}
+  ~ConsumerCloseWorker() {}
+  void Execute() {
+    pulsar_result result = pulsar_consumer_close(this->cConsumer);
+    if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+  }
+  void OnOK() { this->deferred.Resolve(Env().Null()); }
+  void OnError(const Napi::Error &e) {
+    this->deferred.Reject(
+        Napi::Error::New(Env(), std::string("Failed to close consumer: ") + e.Message()).Value());
+  }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_consumer_t *cConsumer;
 };
 
 Napi::Value Consumer::Close(const Napi::CallbackInfo &info) {
-    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
-    ConsumerCloseWorker *wk = new ConsumerCloseWorker(deferred, this->cConsumer);
-    wk->Queue();
-    return deferred.Promise();
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  ConsumerCloseWorker *wk = new ConsumerCloseWorker(deferred, this->cConsumer);
+  wk->Queue();
+  return deferred.Promise();
 }
 
 Consumer::~Consumer() { pulsar_consumer_free(this->cConsumer); }
diff --git a/src/Consumer.h b/src/Consumer.h
index 8e77df3..fe68124 100644
--- a/src/Consumer.h
+++ b/src/Consumer.h
@@ -24,21 +24,21 @@
 #include <pulsar/c/client.h>
 
 class Consumer : public Napi::ObjectWrap<Consumer> {
-   public:
-    static void Init(Napi::Env env, Napi::Object exports);
-    static Napi::Value NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient);
-    static Napi::FunctionReference constructor;
-    Consumer(const Napi::CallbackInfo &info);
-    ~Consumer();
-    void SetCConsumer(pulsar_consumer_t *cConsumer);
+ public:
+  static void Init(Napi::Env env, Napi::Object exports);
+  static Napi::Value NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient);
+  static Napi::FunctionReference constructor;
+  Consumer(const Napi::CallbackInfo &info);
+  ~Consumer();
+  void SetCConsumer(pulsar_consumer_t *cConsumer);
 
-   private:
-    pulsar_consumer_t *cConsumer;
+ private:
+  pulsar_consumer_t *cConsumer;
 
-    Napi::Value Receive(const Napi::CallbackInfo &info);
-    void Acknowledge(const Napi::CallbackInfo &info);
-    void AcknowledgeId(const Napi::CallbackInfo &info);
-    Napi::Value Close(const Napi::CallbackInfo &info);
+  Napi::Value Receive(const Napi::CallbackInfo &info);
+  void Acknowledge(const Napi::CallbackInfo &info);
+  void AcknowledgeId(const Napi::CallbackInfo &info);
+  Napi::Value Close(const Napi::CallbackInfo &info);
 };
 
 #endif
diff --git a/src/ConsumerConfig.cc b/src/ConsumerConfig.cc
index 78f1605..a98bb69 100644
--- a/src/ConsumerConfig.cc
+++ b/src/ConsumerConfig.cc
@@ -37,64 +37,63 @@ static const std::map<std::string, pulsar_consumer_type> SUBSCRIPTION_TYPE = {
 
 ConsumerConfig::ConsumerConfig(const Napi::Object &consumerConfig)
     : topic(""), subscription(""), ackTimeoutMs(0) {
-    this->cConsumerConfig = pulsar_consumer_configuration_create();
+  this->cConsumerConfig = pulsar_consumer_configuration_create();
 
-    if (consumerConfig.Has(CFG_TOPIC) && consumerConfig.Get(CFG_TOPIC).IsString()) {
-        this->topic = consumerConfig.Get(CFG_TOPIC).ToString().Utf8Value();
-    }
+  if (consumerConfig.Has(CFG_TOPIC) && consumerConfig.Get(CFG_TOPIC).IsString()) {
+    this->topic = consumerConfig.Get(CFG_TOPIC).ToString().Utf8Value();
+  }
 
-    if (consumerConfig.Has(CFG_SUBSCRIPTION) && consumerConfig.Get(CFG_SUBSCRIPTION).IsString()) {
-        this->subscription = consumerConfig.Get(CFG_SUBSCRIPTION).ToString().Utf8Value();
-    }
+  if (consumerConfig.Has(CFG_SUBSCRIPTION) && consumerConfig.Get(CFG_SUBSCRIPTION).IsString()) {
+    this->subscription = consumerConfig.Get(CFG_SUBSCRIPTION).ToString().Utf8Value();
+  }
 
-    if (consumerConfig.Has(CFG_SUBSCRIPTION_TYPE) && consumerConfig.Get(CFG_SUBSCRIPTION_TYPE).IsString()) {
-        std::string subscriptionType = consumerConfig.Get(CFG_SUBSCRIPTION_TYPE).ToString().Utf8Value();
-        if (SUBSCRIPTION_TYPE.count(subscriptionType)) {
-            pulsar_consumer_configuration_set_consumer_type(this->cConsumerConfig,
-                                                            SUBSCRIPTION_TYPE.at(subscriptionType));
-        }
+  if (consumerConfig.Has(CFG_SUBSCRIPTION_TYPE) && consumerConfig.Get(CFG_SUBSCRIPTION_TYPE).IsString()) {
+    std::string subscriptionType = consumerConfig.Get(CFG_SUBSCRIPTION_TYPE).ToString().Utf8Value();
+    if (SUBSCRIPTION_TYPE.count(subscriptionType)) {
+      pulsar_consumer_configuration_set_consumer_type(this->cConsumerConfig,
+                                                      SUBSCRIPTION_TYPE.at(subscriptionType));
     }
+  }
 
-    if (consumerConfig.Has(CFG_CONSUMER_NAME) && consumerConfig.Get(CFG_CONSUMER_NAME).IsString()) {
-        std::string consumerName = consumerConfig.Get(CFG_CONSUMER_NAME).ToString().Utf8Value();
-        if (!consumerName.empty())
-            pulsar_consumer_set_consumer_name(this->cConsumerConfig, consumerName.c_str());
-    }
+  if (consumerConfig.Has(CFG_CONSUMER_NAME) && consumerConfig.Get(CFG_CONSUMER_NAME).IsString()) {
+    std::string consumerName = consumerConfig.Get(CFG_CONSUMER_NAME).ToString().Utf8Value();
+    if (!consumerName.empty()) pulsar_consumer_set_consumer_name(this->cConsumerConfig, consumerName.c_str());
+  }
 
-    if (consumerConfig.Has(CFG_ACK_TIMEOUT) && consumerConfig.Get(CFG_ACK_TIMEOUT).IsNumber()) {
-        this->ackTimeoutMs = consumerConfig.Get(CFG_ACK_TIMEOUT).ToNumber().Int64Value();
-        if (this->ackTimeoutMs == 0 || this->ackTimeoutMs >= MIN_ACK_TIMEOUT_MILLIS) {
-            pulsar_consumer_set_unacked_messages_timeout_ms(this->cConsumerConfig, this->ackTimeoutMs);
-        }
+  if (consumerConfig.Has(CFG_ACK_TIMEOUT) && consumerConfig.Get(CFG_ACK_TIMEOUT).IsNumber()) {
+    this->ackTimeoutMs = consumerConfig.Get(CFG_ACK_TIMEOUT).ToNumber().Int64Value();
+    if (this->ackTimeoutMs == 0 || this->ackTimeoutMs >= MIN_ACK_TIMEOUT_MILLIS) {
+      pulsar_consumer_set_unacked_messages_timeout_ms(this->cConsumerConfig, this->ackTimeoutMs);
     }
+  }
 
-    if (consumerConfig.Has(CFG_RECV_QUEUE) && consumerConfig.Get(CFG_RECV_QUEUE).IsNumber()) {
-        int32_t receiverQueueSize = consumerConfig.Get(CFG_RECV_QUEUE).ToNumber().Int32Value();
-        if (receiverQueueSize >= 0) {
-            pulsar_consumer_configuration_set_receiver_queue_size(this->cConsumerConfig, receiverQueueSize);
-        }
+  if (consumerConfig.Has(CFG_RECV_QUEUE) && consumerConfig.Get(CFG_RECV_QUEUE).IsNumber()) {
+    int32_t receiverQueueSize = consumerConfig.Get(CFG_RECV_QUEUE).ToNumber().Int32Value();
+    if (receiverQueueSize >= 0) {
+      pulsar_consumer_configuration_set_receiver_queue_size(this->cConsumerConfig, receiverQueueSize);
     }
+  }
 
-    if (consumerConfig.Has(CFG_RECV_QUEUE_ACROSS_PARTITIONS) &&
-        consumerConfig.Get(CFG_RECV_QUEUE_ACROSS_PARTITIONS).IsNumber()) {
-        int32_t receiverQueueSizeAcrossPartitions =
-            consumerConfig.Get(CFG_RECV_QUEUE_ACROSS_PARTITIONS).ToNumber().Int32Value();
-        if (receiverQueueSizeAcrossPartitions >= 0) {
-            pulsar_consumer_set_max_total_receiver_queue_size_across_partitions(
-                this->cConsumerConfig, receiverQueueSizeAcrossPartitions);
-        }
+  if (consumerConfig.Has(CFG_RECV_QUEUE_ACROSS_PARTITIONS) &&
+      consumerConfig.Get(CFG_RECV_QUEUE_ACROSS_PARTITIONS).IsNumber()) {
+    int32_t receiverQueueSizeAcrossPartitions =
+        consumerConfig.Get(CFG_RECV_QUEUE_ACROSS_PARTITIONS).ToNumber().Int32Value();
+    if (receiverQueueSizeAcrossPartitions >= 0) {
+      pulsar_consumer_set_max_total_receiver_queue_size_across_partitions(this->cConsumerConfig,
+                                                                          receiverQueueSizeAcrossPartitions);
     }
+  }
 
-    if (consumerConfig.Has(CFG_PROPS) && consumerConfig.Get(CFG_PROPS).IsObject()) {
-        Napi::Object propObj = consumerConfig.Get(CFG_PROPS).ToObject();
-        Napi::Array arr = propObj.GetPropertyNames();
-        int size = arr.Length();
-        for (int i = 0; i < size; i++) {
-            std::string key = arr.Get(i).ToString().Utf8Value();
-            std::string value = propObj.Get(key).ToString().Utf8Value();
-            pulsar_consumer_configuration_set_property(this->cConsumerConfig, key.c_str(), value.c_str());
-        }
+  if (consumerConfig.Has(CFG_PROPS) && consumerConfig.Get(CFG_PROPS).IsObject()) {
+    Napi::Object propObj = consumerConfig.Get(CFG_PROPS).ToObject();
+    Napi::Array arr = propObj.GetPropertyNames();
+    int size = arr.Length();
+    for (int i = 0; i < size; i++) {
+      std::string key = arr.Get(i).ToString().Utf8Value();
+      std::string value = propObj.Get(key).ToString().Utf8Value();
+      pulsar_consumer_configuration_set_property(this->cConsumerConfig, key.c_str(), value.c_str());
     }
+  }
 }
 
 ConsumerConfig::~ConsumerConfig() { pulsar_consumer_configuration_free(this->cConsumerConfig); }
diff --git a/src/ConsumerConfig.h b/src/ConsumerConfig.h
index 00c890e..7070bf9 100644
--- a/src/ConsumerConfig.h
+++ b/src/ConsumerConfig.h
@@ -26,19 +26,19 @@
 #define MIN_ACK_TIMEOUT_MILLIS 10000
 
 class ConsumerConfig {
-   public:
-    ConsumerConfig(const Napi::Object &consumerConfig);
-    ~ConsumerConfig();
-    pulsar_consumer_configuration_t *GetCConsumerConfig();
-    std::string GetTopic();
-    std::string GetSubscription();
-    int64_t GetAckTimeoutMs();
+ public:
+  ConsumerConfig(const Napi::Object &consumerConfig);
+  ~ConsumerConfig();
+  pulsar_consumer_configuration_t *GetCConsumerConfig();
+  std::string GetTopic();
+  std::string GetSubscription();
+  int64_t GetAckTimeoutMs();
 
-   private:
-    pulsar_consumer_configuration_t *cConsumerConfig;
-    std::string topic;
-    std::string subscription;
-    int64_t ackTimeoutMs;
+ private:
+  pulsar_consumer_configuration_t *cConsumerConfig;
+  std::string topic;
+  std::string subscription;
+  int64_t ackTimeoutMs;
 };
 
 #endif
diff --git a/src/Message.cc b/src/Message.cc
index 9a0129e..3c527d5 100644
--- a/src/Message.cc
+++ b/src/Message.cc
@@ -31,29 +31,29 @@ static const std::string CFG_REPL_CLUSTERS = "replicationClusters";
 Napi::FunctionReference Message::constructor;
 
 Napi::Object Message::Init(Napi::Env env, Napi::Object exports) {
-    Napi::HandleScope scope(env);
-
-    Napi::Function func = DefineClass(
-        env, "Message",
-        {InstanceMethod("getTopicName", &Message::GetTopicName),
-         InstanceMethod("getProperties", &Message::GetProperties),
-         InstanceMethod("getData", &Message::GetData), InstanceMethod("getMessageId", &Message::GetMessageId),
-         InstanceMethod("getPublishTimestamp", &Message::GetPublishTimestamp),
-         InstanceMethod("getEventTimestamp", &Message::GetEventTimestamp),
-         InstanceMethod("getPartitionKey", &Message::GetPartitionKey)});
-
-    constructor = Napi::Persistent(func);
-    constructor.SuppressDestruct();
-
-    exports.Set("Message", func);
-    return exports;
+  Napi::HandleScope scope(env);
+
+  Napi::Function func = DefineClass(
+      env, "Message",
+      {InstanceMethod("getTopicName", &Message::GetTopicName),
+       InstanceMethod("getProperties", &Message::GetProperties), InstanceMethod("getData", &Message::GetData),
+       InstanceMethod("getMessageId", &Message::GetMessageId),
+       InstanceMethod("getPublishTimestamp", &Message::GetPublishTimestamp),
+       InstanceMethod("getEventTimestamp", &Message::GetEventTimestamp),
+       InstanceMethod("getPartitionKey", &Message::GetPartitionKey)});
+
+  constructor = Napi::Persistent(func);
+  constructor.SuppressDestruct();
+
+  exports.Set("Message", func);
+  return exports;
 }
 
 Napi::Object Message::NewInstance(Napi::Value arg, pulsar_message_t *cMessage) {
-    Napi::Object obj = constructor.New({});
-    Message *msg = Unwrap(obj);
-    msg->cMessage = cMessage;
-    return obj;
+  Napi::Object obj = constructor.New({});
+  Message *msg = Unwrap(obj);
+  msg->cMessage = cMessage;
+  return obj;
 }
 
 Message::Message(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Message>(info), cMessage(nullptr) {}
@@ -61,137 +61,137 @@ Message::Message(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Message>(inf
 pulsar_message_t *Message::GetCMessage() { return this->cMessage; }
 
 Napi::Value Message::GetTopicName(const Napi::CallbackInfo &info) {
-    Napi::Env env = info.Env();
-    if (!ValidateCMessage(env)) {
-        return env.Null();
-    }
-    return Napi::String::New(env, pulsar_message_get_topic_name(this->cMessage));
+  Napi::Env env = info.Env();
+  if (!ValidateCMessage(env)) {
+    return env.Null();
+  }
+  return Napi::String::New(env, pulsar_message_get_topic_name(this->cMessage));
 }
 
 Napi::Value Message::GetProperties(const Napi::CallbackInfo &info) {
-    Napi::Env env = info.Env();
-    if (!ValidateCMessage(env)) {
-        return env.Null();
-    }
-    Napi::Array arr = Napi::Array::New(env);
-    pulsar_string_map_t *cProperties = pulsar_message_get_properties(this->cMessage);
-    int size = pulsar_string_map_size(cProperties);
-    for (int i = 0; i < size; i++) {
-        arr.Set(pulsar_string_map_get_key(cProperties, i), pulsar_string_map_get_value(cProperties, i));
-    }
-    return arr;
+  Napi::Env env = info.Env();
+  if (!ValidateCMessage(env)) {
+    return env.Null();
+  }
+  Napi::Array arr = Napi::Array::New(env);
+  pulsar_string_map_t *cProperties = pulsar_message_get_properties(this->cMessage);
+  int size = pulsar_string_map_size(cProperties);
+  for (int i = 0; i < size; i++) {
+    arr.Set(pulsar_string_map_get_key(cProperties, i), pulsar_string_map_get_value(cProperties, i));
+  }
+  return arr;
 }
 
 Napi::Value Message::GetData(const Napi::CallbackInfo &info) {
-    Napi::Env env = info.Env();
-    if (!ValidateCMessage(env)) {
-        return env.Null();
-    }
-    void *data = const_cast<void *>(pulsar_message_get_data(this->cMessage));
-    size_t size = (size_t)pulsar_message_get_length(this->cMessage);
-    return Napi::Buffer<char>::New(env, (char *)data, size);
+  Napi::Env env = info.Env();
+  if (!ValidateCMessage(env)) {
+    return env.Null();
+  }
+  void *data = const_cast<void *>(pulsar_message_get_data(this->cMessage));
+  size_t size = (size_t)pulsar_message_get_length(this->cMessage);
+  return Napi::Buffer<char>::New(env, (char *)data, size);
 }
 
 Napi::Value Message::GetMessageId(const Napi::CallbackInfo &info) {
-    Napi::Env env = info.Env();
-    if (!ValidateCMessage(env)) {
-        return env.Null();
-    }
-    return MessageId::NewInstanceFromMessage(info, this->cMessage);
+  Napi::Env env = info.Env();
+  if (!ValidateCMessage(env)) {
+    return env.Null();
+  }
+  return MessageId::NewInstanceFromMessage(info, this->cMessage);
 }
 
 Napi::Value Message::GetEventTimestamp(const Napi::CallbackInfo &info) {
-    Napi::Env env = info.Env();
-    if (!ValidateCMessage(env)) {
-        return env.Null();
-    }
-    return Napi::Number::New(env, pulsar_message_get_event_timestamp(this->cMessage));
+  Napi::Env env = info.Env();
+  if (!ValidateCMessage(env)) {
+    return env.Null();
+  }
+  return Napi::Number::New(env, pulsar_message_get_event_timestamp(this->cMessage));
 }
 
 Napi::Value Message::GetPublishTimestamp(const Napi::CallbackInfo &info) {
-    Napi::Env env = info.Env();
-    if (!ValidateCMessage(env)) {
-        return env.Null();
-    }
-    return Napi::Number::New(env, pulsar_message_get_publish_timestamp(this->cMessage));
+  Napi::Env env = info.Env();
+  if (!ValidateCMessage(env)) {
+    return env.Null();
+  }
+  return Napi::Number::New(env, pulsar_message_get_publish_timestamp(this->cMessage));
 }
 
 Napi::Value Message::GetPartitionKey(const Napi::CallbackInfo &info) {
-    Napi::Env env = info.Env();
-    if (!ValidateCMessage(env)) {
-        return env.Null();
-    }
-    return Napi::String::New(env, pulsar_message_get_partitionKey(this->cMessage));
+  Napi::Env env = info.Env();
+  if (!ValidateCMessage(env)) {
+    return env.Null();
+  }
+  return Napi::String::New(env, pulsar_message_get_partitionKey(this->cMessage));
 }
 
 bool Message::ValidateCMessage(Napi::Env env) {
-    if (this->cMessage) {
-        return true;
-    } else {
-        Napi::Error::New(env, "Message has not been built").ThrowAsJavaScriptException();
-        return false;
-    }
+  if (this->cMessage) {
+    return true;
+  } else {
+    Napi::Error::New(env, "Message has not been built").ThrowAsJavaScriptException();
+    return false;
+  }
 }
 
 pulsar_message_t *Message::BuildMessage(Napi::Object conf) {
-    pulsar_message_t *cMessage = pulsar_message_create();
-
-    if (conf.Has(CFG_DATA) && conf.Get(CFG_DATA).IsBuffer()) {
-        Napi::Buffer<char> buf = conf.Get(CFG_DATA).As<Napi::Buffer<char>>();
-        char *data = buf.Data();
-        pulsar_message_set_content(cMessage, data, buf.Length());
-    }
-
-    if (conf.Has(CFG_PROPS) && conf.Get(CFG_PROPS).IsObject()) {
-        Napi::Object propObj = conf.Get(CFG_PROPS).ToObject();
-        Napi::Array arr = propObj.GetPropertyNames();
-        int size = arr.Length();
-        for (int i = 0; i < size; i++) {
-            Napi::String key = arr.Get(i).ToString();
-            Napi::String value = propObj.Get(key).ToString();
-            pulsar_message_set_property(cMessage, key.Utf8Value().c_str(), value.Utf8Value().c_str());
-        }
-    }
-
-    if (conf.Has(CFG_EVENT_TIME) && conf.Get(CFG_EVENT_TIME).IsNumber()) {
-        int64_t eventTimestamp = conf.Get(CFG_EVENT_TIME).ToNumber().Int64Value();
-        if (eventTimestamp >= 0) {
-            pulsar_message_set_event_timestamp(cMessage, eventTimestamp);
-        }
-    }
-
-    if (conf.Has(CFG_SEQUENCE_ID) && conf.Get(CFG_SEQUENCE_ID).IsNumber()) {
-        Napi::Number sequenceId = conf.Get(CFG_SEQUENCE_ID).ToNumber();
-        pulsar_message_set_sequence_id(cMessage, sequenceId.Int64Value());
-    }
-
-    if (conf.Has(CFG_PARTITION_KEY) && conf.Get(CFG_PARTITION_KEY).IsString()) {
-        Napi::String partitionKey = conf.Get(CFG_PARTITION_KEY).ToString();
-        pulsar_message_set_partition_key(cMessage, partitionKey.Utf8Value().c_str());
-    }
-
-    if (conf.Has(CFG_REPL_CLUSTERS) && conf.Get(CFG_REPL_CLUSTERS).IsArray()) {
-        Napi::Array clusters = conf.Get(CFG_REPL_CLUSTERS).As<Napi::Array>();
-        // Empty list means to disable replication
-        int length = clusters.Length();
-        if (length == 0) {
-            pulsar_message_disable_replication(cMessage, 1);
-        } else {
-            char **arr = NewStringArray(length);
-            for (int i = 0; i < length; i++) {
-                SetString(arr, clusters.Get(i).ToString().Utf8Value().c_str(), i);
-            }
-            // TODO: temoporalily commented out unless 2.3.1 which includes interface change of
-            // pulsar_message_set_replication_clusters (#3729) is released
-            // pulsar_message_set_replication_clusters(cMessage, (const char **)arr, length);
-            FreeStringArray(arr, length);
-        }
-    }
-    return cMessage;
+  pulsar_message_t *cMessage = pulsar_message_create();
+
+  if (conf.Has(CFG_DATA) && conf.Get(CFG_DATA).IsBuffer()) {
+    Napi::Buffer<char> buf = conf.Get(CFG_DATA).As<Napi::Buffer<char>>();
+    char *data = buf.Data();
+    pulsar_message_set_content(cMessage, data, buf.Length());
+  }
+
+  if (conf.Has(CFG_PROPS) && conf.Get(CFG_PROPS).IsObject()) {
+    Napi::Object propObj = conf.Get(CFG_PROPS).ToObject();
+    Napi::Array arr = propObj.GetPropertyNames();
+    int size = arr.Length();
+    for (int i = 0; i < size; i++) {
+      Napi::String key = arr.Get(i).ToString();
+      Napi::String value = propObj.Get(key).ToString();
+      pulsar_message_set_property(cMessage, key.Utf8Value().c_str(), value.Utf8Value().c_str());
+    }
+  }
+
+  if (conf.Has(CFG_EVENT_TIME) && conf.Get(CFG_EVENT_TIME).IsNumber()) {
+    int64_t eventTimestamp = conf.Get(CFG_EVENT_TIME).ToNumber().Int64Value();
+    if (eventTimestamp >= 0) {
+      pulsar_message_set_event_timestamp(cMessage, eventTimestamp);
+    }
+  }
+
+  if (conf.Has(CFG_SEQUENCE_ID) && conf.Get(CFG_SEQUENCE_ID).IsNumber()) {
+    Napi::Number sequenceId = conf.Get(CFG_SEQUENCE_ID).ToNumber();
+    pulsar_message_set_sequence_id(cMessage, sequenceId.Int64Value());
+  }
+
+  if (conf.Has(CFG_PARTITION_KEY) && conf.Get(CFG_PARTITION_KEY).IsString()) {
+    Napi::String partitionKey = conf.Get(CFG_PARTITION_KEY).ToString();
+    pulsar_message_set_partition_key(cMessage, partitionKey.Utf8Value().c_str());
+  }
+
+  if (conf.Has(CFG_REPL_CLUSTERS) && conf.Get(CFG_REPL_CLUSTERS).IsArray()) {
+    Napi::Array clusters = conf.Get(CFG_REPL_CLUSTERS).As<Napi::Array>();
+    // Empty list means to disable replication
+    int length = clusters.Length();
+    if (length == 0) {
+      pulsar_message_disable_replication(cMessage, 1);
+    } else {
+      char **arr = NewStringArray(length);
+      for (int i = 0; i < length; i++) {
+        SetString(arr, clusters.Get(i).ToString().Utf8Value().c_str(), i);
+      }
+      // TODO: temoporalily commented out unless 2.3.1 which includes interface change of
+      // pulsar_message_set_replication_clusters (#3729) is released
+      // pulsar_message_set_replication_clusters(cMessage, (const char **)arr, length);
+      FreeStringArray(arr, length);
+    }
+  }
+  return cMessage;
 }
 
 Message::~Message() {
-    if (this->cMessage != nullptr) {
-        pulsar_message_free(this->cMessage);
-    }
+  if (this->cMessage != nullptr) {
+    pulsar_message_free(this->cMessage);
+  }
 }
diff --git a/src/Message.h b/src/Message.h
index 8992f9c..42aa9aa 100644
--- a/src/Message.h
+++ b/src/Message.h
@@ -24,41 +24,41 @@
 #include <pulsar/c/message.h>
 
 class Message : public Napi::ObjectWrap<Message> {
-   public:
-    static Napi::Object Init(Napi::Env env, Napi::Object exports);
-    static Napi::Object NewInstance(Napi::Value arg, pulsar_message_t *cMessage);
-    static pulsar_message_t *BuildMessage(Napi::Object conf);
-    Message(const Napi::CallbackInfo &info);
-    ~Message();
-    pulsar_message_t *GetCMessage();
+ public:
+  static Napi::Object Init(Napi::Env env, Napi::Object exports);
+  static Napi::Object NewInstance(Napi::Value arg, pulsar_message_t *cMessage);
+  static pulsar_message_t *BuildMessage(Napi::Object conf);
+  Message(const Napi::CallbackInfo &info);
+  ~Message();
+  pulsar_message_t *GetCMessage();
 
-   private:
-    static Napi::FunctionReference constructor;
+ private:
+  static Napi::FunctionReference constructor;
 
-    pulsar_message_t *cMessage;
+  pulsar_message_t *cMessage;
 
-    Napi::Value GetTopicName(const Napi::CallbackInfo &info);
-    Napi::Value GetProperties(const Napi::CallbackInfo &info);
-    Napi::Value GetData(const Napi::CallbackInfo &info);
-    Napi::Value GetMessageId(const Napi::CallbackInfo &info);
-    Napi::Value GetPublishTimestamp(const Napi::CallbackInfo &info);
-    Napi::Value GetEventTimestamp(const Napi::CallbackInfo &info);
-    Napi::Value GetPartitionKey(const Napi::CallbackInfo &info);
-    bool ValidateCMessage(Napi::Env env);
+  Napi::Value GetTopicName(const Napi::CallbackInfo &info);
+  Napi::Value GetProperties(const Napi::CallbackInfo &info);
+  Napi::Value GetData(const Napi::CallbackInfo &info);
+  Napi::Value GetMessageId(const Napi::CallbackInfo &info);
+  Napi::Value GetPublishTimestamp(const Napi::CallbackInfo &info);
+  Napi::Value GetEventTimestamp(const Napi::CallbackInfo &info);
+  Napi::Value GetPartitionKey(const Napi::CallbackInfo &info);
+  bool ValidateCMessage(Napi::Env env);
 
-    static char **NewStringArray(int size) { return (char **)calloc(sizeof(char *), size); }
-    static void SetString(char **array, const char *str, int n) {
-        char *copied = (char *)calloc(strlen(str) + 1, sizeof(char));
-        strcpy(copied, str);
-        array[n] = copied;
-    }
-    static void FreeStringArray(char **array, int size) {
-        int i;
-        for (i = 0; i < size; i++) {
-            free(array[i]);
-        }
-        free(array);
+  static char **NewStringArray(int size) { return (char **)calloc(sizeof(char *), size); }
+  static void SetString(char **array, const char *str, int n) {
+    char *copied = (char *)calloc(strlen(str) + 1, sizeof(char));
+    strcpy(copied, str);
+    array[n] = copied;
+  }
+  static void FreeStringArray(char **array, int size) {
+    int i;
+    for (i = 0; i < size; i++) {
+      free(array[i]);
     }
+    free(array);
+  }
 };
 
 #endif
diff --git a/src/MessageId.cc b/src/MessageId.cc
index 3a95f43..a0b520f 100644
--- a/src/MessageId.cc
+++ b/src/MessageId.cc
@@ -24,64 +24,64 @@
 Napi::FunctionReference MessageId::constructor;
 
 Napi::Object MessageId::Init(Napi::Env env, Napi::Object exports) {
-    Napi::HandleScope scope(env);
+  Napi::HandleScope scope(env);
 
-    Napi::Function func = DefineClass(env, "MessageId",
-                                      {
-                                          StaticMethod("earliest", &MessageId::Earliest, napi_static),
-                                          StaticMethod("latest", &MessageId::Latest, napi_static),
-                                          StaticMethod("finalize", &MessageId::Finalize, napi_static),
-                                          InstanceMethod("toString", &MessageId::ToString),
-                                      });
+  Napi::Function func = DefineClass(env, "MessageId",
+                                    {
+                                        StaticMethod("earliest", &MessageId::Earliest, napi_static),
+                                        StaticMethod("latest", &MessageId::Latest, napi_static),
+                                        StaticMethod("finalize", &MessageId::Finalize, napi_static),
+                                        InstanceMethod("toString", &MessageId::ToString),
+                                    });
 
-    constructor = Napi::Persistent(func);
-    constructor.SuppressDestruct();
+  constructor = Napi::Persistent(func);
+  constructor.SuppressDestruct();
 
-    exports.Set("MessageId", func);
-    return exports;
+  exports.Set("MessageId", func);
+  return exports;
 }
 
 MessageId::MessageId(const Napi::CallbackInfo &info) : Napi::ObjectWrap<MessageId>(info) {
-    Napi::Env env = info.Env();
-    Napi::HandleScope scope(env);
+  Napi::Env env = info.Env();
+  Napi::HandleScope scope(env);
 }
 
 Napi::Object MessageId::NewInstanceFromMessage(const Napi::CallbackInfo &info, pulsar_message_t *cMessage) {
-    Napi::Object obj = NewInstance(info[0]);
-    MessageId *msgId = Unwrap(obj);
-    msgId->cMessageId = pulsar_message_get_message_id(cMessage);
-    return obj;
+  Napi::Object obj = NewInstance(info[0]);
+  MessageId *msgId = Unwrap(obj);
+  msgId->cMessageId = pulsar_message_get_message_id(cMessage);
+  return obj;
 }
 
 Napi::Object MessageId::NewInstance(Napi::Value arg) {
-    Napi::Object obj = constructor.New({arg});
-    return obj;
+  Napi::Object obj = constructor.New({arg});
+  return obj;
 }
 
 void MessageId::Finalize(const Napi::CallbackInfo &info) {
-    Napi::Object obj = info[0].As<Napi::Object>();
-    MessageId *msgId = Unwrap(obj);
-    pulsar_message_id_free(msgId->cMessageId);
+  Napi::Object obj = info[0].As<Napi::Object>();
+  MessageId *msgId = Unwrap(obj);
+  pulsar_message_id_free(msgId->cMessageId);
 }
 
 Napi::Value MessageId::Earliest(const Napi::CallbackInfo &info) {
-    Napi::Object obj = NewInstance(info[0]);
-    MessageId *msgId = Unwrap(obj);
-    msgId->cMessageId = (pulsar_message_id_t *)pulsar_message_id_earliest();
-    return obj;
+  Napi::Object obj = NewInstance(info[0]);
+  MessageId *msgId = Unwrap(obj);
+  msgId->cMessageId = (pulsar_message_id_t *)pulsar_message_id_earliest();
+  return obj;
 }
 
 Napi::Value MessageId::Latest(const Napi::CallbackInfo &info) {
-    Napi::Object obj = NewInstance(info[0]);
-    MessageId *msgId = Unwrap(obj);
-    msgId->cMessageId = (pulsar_message_id_t *)pulsar_message_id_latest();
-    return obj;
+  Napi::Object obj = NewInstance(info[0]);
+  MessageId *msgId = Unwrap(obj);
+  msgId->cMessageId = (pulsar_message_id_t *)pulsar_message_id_latest();
+  return obj;
 }
 
 pulsar_message_id_t *MessageId::GetCMessageId() { return this->cMessageId; }
 
 Napi::Value MessageId::ToString(const Napi::CallbackInfo &info) {
-    return Napi::String::New(info.Env(), pulsar_message_id_str(this->cMessageId));
+  return Napi::String::New(info.Env(), pulsar_message_id_str(this->cMessageId));
 }
 
 MessageId::~MessageId() { pulsar_message_id_free(this->cMessageId); }
diff --git a/src/MessageId.h b/src/MessageId.h
index 761f04b..09c59e2 100644
--- a/src/MessageId.h
+++ b/src/MessageId.h
@@ -25,22 +25,22 @@
 #include <pulsar/c/message_id.h>
 
 class MessageId : public Napi::ObjectWrap<MessageId> {
-   public:
-    static Napi::Object Init(Napi::Env env, Napi::Object exports);
-    static Napi::Object NewInstance(Napi::Value arg);
-    static Napi::Object NewInstanceFromMessage(const Napi::CallbackInfo &info, pulsar_message_t *cMessage);
-    static Napi::Value Earliest(const Napi::CallbackInfo &info);
-    static Napi::Value Latest(const Napi::CallbackInfo &info);
-    static void Finalize(const Napi::CallbackInfo &info);
-    MessageId(const Napi::CallbackInfo &info);
-    ~MessageId();
-    pulsar_message_id_t *GetCMessageId();
+ public:
+  static Napi::Object Init(Napi::Env env, Napi::Object exports);
+  static Napi::Object NewInstance(Napi::Value arg);
+  static Napi::Object NewInstanceFromMessage(const Napi::CallbackInfo &info, pulsar_message_t *cMessage);
+  static Napi::Value Earliest(const Napi::CallbackInfo &info);
+  static Napi::Value Latest(const Napi::CallbackInfo &info);
+  static void Finalize(const Napi::CallbackInfo &info);
+  MessageId(const Napi::CallbackInfo &info);
+  ~MessageId();
+  pulsar_message_id_t *GetCMessageId();
 
-   private:
-    static Napi::FunctionReference constructor;
-    pulsar_message_id_t *cMessageId;
+ private:
+  static Napi::FunctionReference constructor;
+  pulsar_message_id_t *cMessageId;
 
-    Napi::Value ToString(const Napi::CallbackInfo &info);
+  Napi::Value ToString(const Napi::CallbackInfo &info);
 };
 
 #endif
diff --git a/src/Producer.cc b/src/Producer.cc
index 9bdb5d4..a19d828 100644
--- a/src/Producer.cc
+++ b/src/Producer.cc
@@ -25,131 +25,127 @@
 Napi::FunctionReference Producer::constructor;
 
 void Producer::Init(Napi::Env env, Napi::Object exports) {
-    Napi::HandleScope scope(env);
+  Napi::HandleScope scope(env);
 
-    Napi::Function func =
-        DefineClass(env, "Producer",
-                    {InstanceMethod("send", &Producer::Send), InstanceMethod("close", &Producer::Close)});
+  Napi::Function func = DefineClass(
+      env, "Producer", {InstanceMethod("send", &Producer::Send), InstanceMethod("close", &Producer::Close)});
 
-    constructor = Napi::Persistent(func);
-    constructor.SuppressDestruct();
+  constructor = Napi::Persistent(func);
+  constructor.SuppressDestruct();
 }
 
 void Producer::SetCProducer(pulsar_producer_t *cProducer) { this->cProducer = cProducer; }
 
 class ProducerNewInstanceWorker : public Napi::AsyncWorker {
-   public:
-    ProducerNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
-                              ProducerConfig *producerConfig)
-        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
-          deferred(deferred),
-          cClient(cClient),
-          producerConfig(producerConfig) {}
-    ~ProducerNewInstanceWorker() {}
-    void Execute() {
-        const std::string &topic = this->producerConfig->GetTopic();
-        if (topic.empty()) {
-            SetError(
-                std::string("Topic is required and must be specified as a string when creating producer"));
-            return;
-        }
-
-        pulsar_result result = pulsar_client_create_producer(
-            this->cClient, topic.c_str(), this->producerConfig->GetCProducerConfig(), &(this->cProducer));
-        delete this->producerConfig;
-        if (result != pulsar_result_Ok) {
-            SetError(std::string("Failed to create producer: ") + pulsar_result_str(result));
-            return;
-        }
-    }
-    void OnOK() {
-        Napi::Object obj = Producer::constructor.New({});
-        Producer *producer = Producer::Unwrap(obj);
-        producer->SetCProducer(this->cProducer);
-        this->deferred.Resolve(obj);
-    }
-    void OnError(const Napi::Error &e) {
-        this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value());
+ public:
+  ProducerNewInstanceWorker(const Napi::Promise::Deferred &deferred, pulsar_client_t *cClient,
+                            ProducerConfig *producerConfig)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cClient(cClient),
+        producerConfig(producerConfig) {}
+  ~ProducerNewInstanceWorker() {}
+  void Execute() {
+    const std::string &topic = this->producerConfig->GetTopic();
+    if (topic.empty()) {
+      SetError(std::string("Topic is required and must be specified as a string when creating producer"));
+      return;
     }
 
-   private:
-    Napi::Promise::Deferred deferred;
-    pulsar_client_t *cClient;
-    ProducerConfig *producerConfig;
-    pulsar_producer_t *cProducer;
+    pulsar_result result = pulsar_client_create_producer(
+        this->cClient, topic.c_str(), this->producerConfig->GetCProducerConfig(), &(this->cProducer));
+    delete this->producerConfig;
+    if (result != pulsar_result_Ok) {
+      SetError(std::string("Failed to create producer: ") + pulsar_result_str(result));
+      return;
+    }
+  }
+  void OnOK() {
+    Napi::Object obj = Producer::constructor.New({});
+    Producer *producer = Producer::Unwrap(obj);
+    producer->SetCProducer(this->cProducer);
+    this->deferred.Resolve(obj);
+  }
+  void OnError(const Napi::Error &e) { this->deferred.Reject(Napi::Error::New(Env(), e.Message()).Value()); }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_client_t *cClient;
+  ProducerConfig *producerConfig;
+  pulsar_producer_t *cProducer;
 };
 
 Napi::Value Producer::NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient) {
-    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
-    Napi::Object config = info[0].As<Napi::Object>();
-    ProducerConfig *producerConfig = new ProducerConfig(config);
-    ProducerNewInstanceWorker *wk = new ProducerNewInstanceWorker(deferred, cClient, producerConfig);
-    wk->Queue();
-    return deferred.Promise();
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  Napi::Object config = info[0].As<Napi::Object>();
+  ProducerConfig *producerConfig = new ProducerConfig(config);
+  ProducerNewInstanceWorker *wk = new ProducerNewInstanceWorker(deferred, cClient, producerConfig);
+  wk->Queue();
+  return deferred.Promise();
 }
 
 Producer::Producer(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Producer>(info) {}
 
 class ProducerSendWorker : public Napi::AsyncWorker {
-   public:
-    ProducerSendWorker(const Napi::Promise::Deferred &deferred, pulsar_producer_t *cProducer,
-                       pulsar_message_t *cMessage)
-        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
-          deferred(deferred),
-          cProducer(cProducer),
-          cMessage(cMessage) {}
-    ~ProducerSendWorker() { pulsar_message_free(this->cMessage); }
-    void Execute() {
-        pulsar_result result = pulsar_producer_send(this->cProducer, this->cMessage);
-        if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
-    }
-    void OnOK() { this->deferred.Resolve(Env().Null()); }
-    void OnError(const Napi::Error &e) {
-        this->deferred.Reject(
-            Napi::Error::New(Env(), std::string("Failed to send message: ") + e.Message()).Value());
-    }
-
-   private:
-    Napi::Promise::Deferred deferred;
-    pulsar_producer_t *cProducer;
-    pulsar_message_t *cMessage;
+ public:
+  ProducerSendWorker(const Napi::Promise::Deferred &deferred, pulsar_producer_t *cProducer,
+                     pulsar_message_t *cMessage)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cProducer(cProducer),
+        cMessage(cMessage) {}
+  ~ProducerSendWorker() { pulsar_message_free(this->cMessage); }
+  void Execute() {
+    pulsar_result result = pulsar_producer_send(this->cProducer, this->cMessage);
+    if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+  }
+  void OnOK() { this->deferred.Resolve(Env().Null()); }
+  void OnError(const Napi::Error &e) {
+    this->deferred.Reject(
+        Napi::Error::New(Env(), std::string("Failed to send message: ") + e.Message()).Value());
+  }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_producer_t *cProducer;
+  pulsar_message_t *cMessage;
 };
 
 Napi::Value Producer::Send(const Napi::CallbackInfo &info) {
-    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
-    pulsar_message_t *cMessage = Message::BuildMessage(info[0].As<Napi::Object>());
-    ProducerSendWorker *wk = new ProducerSendWorker(deferred, this->cProducer, cMessage);
-    wk->Queue();
-    return deferred.Promise();
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  pulsar_message_t *cMessage = Message::BuildMessage(info[0].As<Napi::Object>());
+  ProducerSendWorker *wk = new ProducerSendWorker(deferred, this->cProducer, cMessage);
+  wk->Queue();
+  return deferred.Promise();
 }
 
 class ProducerCloseWorker : public Napi::AsyncWorker {
-   public:
-    ProducerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_producer_t *cProducer)
-        : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
-          deferred(deferred),
-          cProducer(cProducer) {}
-    ~ProducerCloseWorker() {}
-    void Execute() {
-        pulsar_result result = pulsar_producer_close(this->cProducer);
-        if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
-    }
-    void OnOK() { this->deferred.Resolve(Env().Null()); }
-    void OnError(const Napi::Error &e) {
-        this->deferred.Reject(
-            Napi::Error::New(Env(), std::string("Failed to close producer: ") + e.Message()).Value());
-    }
-
-   private:
-    Napi::Promise::Deferred deferred;
-    pulsar_producer_t *cProducer;
+ public:
+  ProducerCloseWorker(const Napi::Promise::Deferred &deferred, pulsar_producer_t *cProducer)
+      : AsyncWorker(Napi::Function::New(deferred.Promise().Env(), [](const Napi::CallbackInfo &info) {})),
+        deferred(deferred),
+        cProducer(cProducer) {}
+  ~ProducerCloseWorker() {}
+  void Execute() {
+    pulsar_result result = pulsar_producer_close(this->cProducer);
+    if (result != pulsar_result_Ok) SetError(pulsar_result_str(result));
+  }
+  void OnOK() { this->deferred.Resolve(Env().Null()); }
+  void OnError(const Napi::Error &e) {
+    this->deferred.Reject(
+        Napi::Error::New(Env(), std::string("Failed to close producer: ") + e.Message()).Value());
+  }
+
+ private:
+  Napi::Promise::Deferred deferred;
+  pulsar_producer_t *cProducer;
 };
 
 Napi::Value Producer::Close(const Napi::CallbackInfo &info) {
-    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
-    ProducerCloseWorker *wk = new ProducerCloseWorker(deferred, this->cProducer);
-    wk->Queue();
-    return deferred.Promise();
+  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+  ProducerCloseWorker *wk = new ProducerCloseWorker(deferred, this->cProducer);
+  wk->Queue();
+  return deferred.Promise();
 }
 
 Producer::~Producer() { pulsar_producer_free(this->cProducer); }
diff --git a/src/Producer.h b/src/Producer.h
index c758ce4..5d31cfc 100644
--- a/src/Producer.h
+++ b/src/Producer.h
@@ -25,18 +25,18 @@
 #include <pulsar/c/producer.h>
 
 class Producer : public Napi::ObjectWrap<Producer> {
-   public:
-    static void Init(Napi::Env env, Napi::Object exports);
-    static Napi::Value NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient);
-    static Napi::FunctionReference constructor;
-    Producer(const Napi::CallbackInfo &info);
-    ~Producer();
-    void SetCProducer(pulsar_producer_t *cProducer);
+ public:
+  static void Init(Napi::Env env, Napi::Object exports);
+  static Napi::Value NewInstance(const Napi::CallbackInfo &info, pulsar_client_t *cClient);
+  static Napi::FunctionReference constructor;
+  Producer(const Napi::CallbackInfo &info);
+  ~Producer();
+  void SetCProducer(pulsar_producer_t *cProducer);
 
-   private:
-    pulsar_producer_t *cProducer;
-    Napi::Value Send(const Napi::CallbackInfo &info);
-    Napi::Value Close(const Napi::CallbackInfo &info);
+ private:
+  pulsar_producer_t *cProducer;
+  Napi::Value Send(const Napi::CallbackInfo &info);
+  Napi::Value Close(const Napi::CallbackInfo &info);
 };
 
 #endif
diff --git a/src/ProducerConfig.cc b/src/ProducerConfig.cc
index 9b94dd2..76fd25c 100644
--- a/src/ProducerConfig.cc
+++ b/src/ProducerConfig.cc
@@ -50,106 +50,105 @@ static std::map<std::string, pulsar_compression_type> COMPRESSION_TYPE = {{"Zlib
                                                                           {"LZ4", pulsar_CompressionLZ4}};
 
 ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
-    this->cProducerConfig = pulsar_producer_configuration_create();
-
-    if (producerConfig.Has(CFG_TOPIC) && producerConfig.Get(CFG_TOPIC).IsString()) {
-        this->topic = producerConfig.Get(CFG_TOPIC).ToString().Utf8Value();
-    }
-
-    if (producerConfig.Has(CFG_PRODUCER_NAME) && producerConfig.Get(CFG_PRODUCER_NAME).IsString()) {
-        std::string producerName = producerConfig.Get(CFG_PRODUCER_NAME).ToString().Utf8Value();
-        if (!producerName.empty())
-            pulsar_producer_configuration_set_producer_name(this->cProducerConfig, producerName.c_str());
-    }
-
-    if (producerConfig.Has(CFG_SEND_TIMEOUT) && producerConfig.Get(CFG_SEND_TIMEOUT).IsNumber()) {
-        int32_t sendTimeoutMs = producerConfig.Get(CFG_SEND_TIMEOUT).ToNumber().Int32Value();
-        if (sendTimeoutMs > 0) {
-            pulsar_producer_configuration_set_send_timeout(this->cProducerConfig, sendTimeoutMs);
-        }
+  this->cProducerConfig = pulsar_producer_configuration_create();
+
+  if (producerConfig.Has(CFG_TOPIC) && producerConfig.Get(CFG_TOPIC).IsString()) {
+    this->topic = producerConfig.Get(CFG_TOPIC).ToString().Utf8Value();
+  }
+
+  if (producerConfig.Has(CFG_PRODUCER_NAME) && producerConfig.Get(CFG_PRODUCER_NAME).IsString()) {
+    std::string producerName = producerConfig.Get(CFG_PRODUCER_NAME).ToString().Utf8Value();
+    if (!producerName.empty())
+      pulsar_producer_configuration_set_producer_name(this->cProducerConfig, producerName.c_str());
+  }
+
+  if (producerConfig.Has(CFG_SEND_TIMEOUT) && producerConfig.Get(CFG_SEND_TIMEOUT).IsNumber()) {
+    int32_t sendTimeoutMs = producerConfig.Get(CFG_SEND_TIMEOUT).ToNumber().Int32Value();
+    if (sendTimeoutMs > 0) {
+      pulsar_producer_configuration_set_send_timeout(this->cProducerConfig, sendTimeoutMs);
     }
+  }
 
-    if (producerConfig.Has(CFG_INIT_SEQUENCE_ID) && producerConfig.Get(CFG_INIT_SEQUENCE_ID).IsNumber()) {
-        int64_t initialSequenceId = producerConfig.Get(CFG_INIT_SEQUENCE_ID).ToNumber().Int64Value();
-        pulsar_producer_configuration_set_initial_sequence_id(this->cProducerConfig, initialSequenceId);
-    }
+  if (producerConfig.Has(CFG_INIT_SEQUENCE_ID) && producerConfig.Get(CFG_INIT_SEQUENCE_ID).IsNumber()) {
+    int64_t initialSequenceId = producerConfig.Get(CFG_INIT_SEQUENCE_ID).ToNumber().Int64Value();
+    pulsar_producer_configuration_set_initial_sequence_id(this->cProducerConfig, initialSequenceId);
+  }
 
-    if (producerConfig.Has(CFG_MAX_PENDING) && producerConfig.Get(CFG_MAX_PENDING).IsNumber()) {
-        int32_t maxPendingMessages = producerConfig.Get(CFG_MAX_PENDING).ToNumber().Int32Value();
-        if (maxPendingMessages > 0) {
-            pulsar_producer_configuration_set_max_pending_messages(this->cProducerConfig, maxPendingMessages);
-        }
+  if (producerConfig.Has(CFG_MAX_PENDING) && producerConfig.Get(CFG_MAX_PENDING).IsNumber()) {
+    int32_t maxPendingMessages = producerConfig.Get(CFG_MAX_PENDING).ToNumber().Int32Value();
+    if (maxPendingMessages > 0) {
+      pulsar_producer_configuration_set_max_pending_messages(this->cProducerConfig, maxPendingMessages);
     }
-
-    if (producerConfig.Has(CFG_MAX_PENDING_ACROSS_PARTITIONS) &&
-        producerConfig.Get(CFG_MAX_PENDING_ACROSS_PARTITIONS).IsNumber()) {
-        int32_t maxPendingMessagesAcrossPartitions =
-            producerConfig.Get(CFG_MAX_PENDING_ACROSS_PARTITIONS).ToNumber().Int32Value();
-        if (maxPendingMessagesAcrossPartitions > 0) {
-            pulsar_producer_configuration_set_max_pending_messages(this->cProducerConfig,
-                                                                   maxPendingMessagesAcrossPartitions);
-        }
+  }
+
+  if (producerConfig.Has(CFG_MAX_PENDING_ACROSS_PARTITIONS) &&
+      producerConfig.Get(CFG_MAX_PENDING_ACROSS_PARTITIONS).IsNumber()) {
+    int32_t maxPendingMessagesAcrossPartitions =
+        producerConfig.Get(CFG_MAX_PENDING_ACROSS_PARTITIONS).ToNumber().Int32Value();
+    if (maxPendingMessagesAcrossPartitions > 0) {
+      pulsar_producer_configuration_set_max_pending_messages(this->cProducerConfig,
+                                                             maxPendingMessagesAcrossPartitions);
     }
-
-    if (producerConfig.Has(CFG_BLOCK_IF_QUEUE_FULL) &&
-        producerConfig.Get(CFG_BLOCK_IF_QUEUE_FULL).IsBoolean()) {
-        bool blockIfQueueFull = producerConfig.Get(CFG_BLOCK_IF_QUEUE_FULL).ToBoolean().Value();
-        pulsar_producer_configuration_set_block_if_queue_full(this->cProducerConfig, blockIfQueueFull);
-    }
-
-    if (producerConfig.Has(CFG_ROUTING_MODE) && producerConfig.Get(CFG_ROUTING_MODE).IsString()) {
-        std::string messageRoutingMode = producerConfig.Get(CFG_ROUTING_MODE).ToString().Utf8Value();
-        if (MESSAGE_ROUTING_MODE.count(messageRoutingMode))
-            pulsar_producer_configuration_set_partitions_routing_mode(
-                this->cProducerConfig, MESSAGE_ROUTING_MODE.at(messageRoutingMode));
+  }
+
+  if (producerConfig.Has(CFG_BLOCK_IF_QUEUE_FULL) &&
+      producerConfig.Get(CFG_BLOCK_IF_QUEUE_FULL).IsBoolean()) {
+    bool blockIfQueueFull = producerConfig.Get(CFG_BLOCK_IF_QUEUE_FULL).ToBoolean().Value();
+    pulsar_producer_configuration_set_block_if_queue_full(this->cProducerConfig, blockIfQueueFull);
+  }
+
+  if (producerConfig.Has(CFG_ROUTING_MODE) && producerConfig.Get(CFG_ROUTING_MODE).IsString()) {
+    std::string messageRoutingMode = producerConfig.Get(CFG_ROUTING_MODE).ToString().Utf8Value();
+    if (MESSAGE_ROUTING_MODE.count(messageRoutingMode))
+      pulsar_producer_configuration_set_partitions_routing_mode(this->cProducerConfig,
+                                                                MESSAGE_ROUTING_MODE.at(messageRoutingMode));
+  }
+
+  if (producerConfig.Has(CFG_HASH_SCHEME) && producerConfig.Get(CFG_HASH_SCHEME).IsString()) {
+    std::string hashingScheme = producerConfig.Get(CFG_HASH_SCHEME).ToString().Utf8Value();
+    if (HASHING_SCHEME.count(hashingScheme))
+      pulsar_producer_configuration_set_hashing_scheme(this->cProducerConfig,
+                                                       HASHING_SCHEME.at(hashingScheme));
+  }
+
+  if (producerConfig.Has(CFG_COMPRESS_TYPE) && producerConfig.Get(CFG_COMPRESS_TYPE).IsString()) {
+    std::string compressionType = producerConfig.Get(CFG_COMPRESS_TYPE).ToString().Utf8Value();
+    if (COMPRESSION_TYPE.count(compressionType))
+      pulsar_producer_configuration_set_compression_type(this->cProducerConfig,
+                                                         COMPRESSION_TYPE.at(compressionType));
+  }
+
+  if (producerConfig.Has(CFG_BATCH_ENABLED) && producerConfig.Get(CFG_BATCH_ENABLED).IsBoolean()) {
+    bool batchingEnabled = producerConfig.Get(CFG_BATCH_ENABLED).ToBoolean().Value();
+    pulsar_producer_configuration_set_batching_enabled(this->cProducerConfig, batchingEnabled);
+  }
+
+  if (producerConfig.Has(CFG_BATCH_MAX_DELAY) && producerConfig.Get(CFG_BATCH_MAX_DELAY).IsNumber()) {
+    int64_t batchingMaxPublishDelayMs = producerConfig.Get(CFG_BATCH_MAX_DELAY).ToNumber().Int64Value();
+    if (batchingMaxPublishDelayMs > 0) {
+      pulsar_producer_configuration_set_batching_max_publish_delay_ms(this->cProducerConfig,
+                                                                      (long)batchingMaxPublishDelayMs);
     }
+  }
 
-    if (producerConfig.Has(CFG_HASH_SCHEME) && producerConfig.Get(CFG_HASH_SCHEME).IsString()) {
-        std::string hashingScheme = producerConfig.Get(CFG_HASH_SCHEME).ToString().Utf8Value();
-        if (HASHING_SCHEME.count(hashingScheme))
-            pulsar_producer_configuration_set_hashing_scheme(this->cProducerConfig,
-                                                             HASHING_SCHEME.at(hashingScheme));
+  if (producerConfig.Has(CFG_BATCH_MAX_MSG) && producerConfig.Get(CFG_BATCH_MAX_MSG).IsNumber()) {
+    uint32_t batchingMaxMessages = producerConfig.Get(CFG_BATCH_MAX_MSG).ToNumber().Uint32Value();
+    if (batchingMaxMessages > 0) {
+      pulsar_producer_configuration_set_batching_max_messages(this->cProducerConfig, batchingMaxMessages);
     }
-
-    if (producerConfig.Has(CFG_COMPRESS_TYPE) && producerConfig.Get(CFG_COMPRESS_TYPE).IsString()) {
-        std::string compressionType = producerConfig.Get(CFG_COMPRESS_TYPE).ToString().Utf8Value();
-        if (COMPRESSION_TYPE.count(compressionType))
-            pulsar_producer_configuration_set_compression_type(this->cProducerConfig,
-                                                               COMPRESSION_TYPE.at(compressionType));
-    }
-
-    if (producerConfig.Has(CFG_BATCH_ENABLED) && producerConfig.Get(CFG_BATCH_ENABLED).IsBoolean()) {
-        bool batchingEnabled = producerConfig.Get(CFG_BATCH_ENABLED).ToBoolean().Value();
-        pulsar_producer_configuration_set_batching_enabled(this->cProducerConfig, batchingEnabled);
-    }
-
-    if (producerConfig.Has(CFG_BATCH_MAX_DELAY) && producerConfig.Get(CFG_BATCH_MAX_DELAY).IsNumber()) {
-        int64_t batchingMaxPublishDelayMs = producerConfig.Get(CFG_BATCH_MAX_DELAY).ToNumber().Int64Value();
-        if (batchingMaxPublishDelayMs > 0) {
-            pulsar_producer_configuration_set_batching_max_publish_delay_ms(this->cProducerConfig,
-                                                                            (long)batchingMaxPublishDelayMs);
-        }
-    }
-
-    if (producerConfig.Has(CFG_BATCH_MAX_MSG) && producerConfig.Get(CFG_BATCH_MAX_MSG).IsNumber()) {
-        uint32_t batchingMaxMessages = producerConfig.Get(CFG_BATCH_MAX_MSG).ToNumber().Uint32Value();
-        if (batchingMaxMessages > 0) {
-            pulsar_producer_configuration_set_batching_max_messages(this->cProducerConfig,
-                                                                    batchingMaxMessages);
-        }
-    }
-
-    if (producerConfig.Has(CFG_PROPS) && producerConfig.Get(CFG_PROPS).IsObject()) {
-        Napi::Object propObj = producerConfig.Get(CFG_PROPS).ToObject();
-        Napi::Array arr = propObj.GetPropertyNames();
-        int size = arr.Length();
-        for (int i = 0; i < size; i++) {
-            Napi::String key = arr.Get(i).ToString();
-            Napi::String value = propObj.Get(key).ToString();
-            pulsar_producer_configuration_set_property(this->cProducerConfig, key.Utf8Value().c_str(),
-                                                       value.Utf8Value().c_str());
-        }
+  }
+
+  if (producerConfig.Has(CFG_PROPS) && producerConfig.Get(CFG_PROPS).IsObject()) {
+    Napi::Object propObj = producerConfig.Get(CFG_PROPS).ToObject();
+    Napi::Array arr = propObj.GetPropertyNames();
+    int size = arr.Length();
+    for (int i = 0; i < size; i++) {
+      Napi::String key = arr.Get(i).ToString();
+      Napi::String value = propObj.Get(key).ToString();
+      pulsar_producer_configuration_set_property(this->cProducerConfig, key.Utf8Value().c_str(),
+                                                 value.Utf8Value().c_str());
     }
+  }
 }
 
 ProducerConfig::~ProducerConfig() { pulsar_producer_configuration_free(this->cProducerConfig); }
diff --git a/src/ProducerConfig.h b/src/ProducerConfig.h
index e2ecad0..c18c813 100644
--- a/src/ProducerConfig.h
+++ b/src/ProducerConfig.h
@@ -24,15 +24,15 @@
 #include <pulsar/c/producer_configuration.h>
 
 class ProducerConfig {
-   public:
-    ProducerConfig(const Napi::Object &producerConfig);
-    ~ProducerConfig();
-    pulsar_producer_configuration_t *GetCProducerConfig();
-    std::string GetTopic();
+ public:
+  ProducerConfig(const Napi::Object &producerConfig);
+  ~ProducerConfig();
+  pulsar_producer_configuration_t *GetCProducerConfig();
+  std::string GetTopic();
 
-   private:
-    pulsar_producer_configuration_t *cProducerConfig;
-    std::string topic;
+ private:
+  pulsar_producer_configuration_t *cProducerConfig;
+  std::string topic;
 };
 
 #endif
diff --git a/src/addon.cc b/src/addon.cc
index abcbb24..9e75d3f 100644
--- a/src/addon.cc
+++ b/src/addon.cc
@@ -25,11 +25,11 @@
 #include <napi.h>
 
 Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
-    Message::Init(env, exports);
-    MessageId::Init(env, exports);
-    Producer::Init(env, exports);
-    Consumer::Init(env, exports);
-    return Client::Init(env, exports);
+  Message::Init(env, exports);
+  MessageId::Init(env, exports);
+  Producer::Init(env, exports);
+  Consumer::Init(env, exports);
+  return Client::Init(env, exports);
 }
 
 NODE_API_MODULE(NODE_GYP_MODULE_NAME, InitAll)