diff --git a/.eslintrc b/.eslintrc
index c07a4ea1..b418bd8c 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,6 +1,7 @@
{
"env": {
- "node": true
+ "node": true,
+ "es2020": true
},
"extends": "eslint:recommended",
"rules": {
@@ -10,6 +11,7 @@
"no-console": "off",
"no-trailing-spaces": ["error", { "skipBlankLines": true }],
"no-unused-vars": "warn",
+ "no-var": "warn",
"quotes": ["warn", "single", "avoid-escape"],
"semi": ["error", "always"],
"space-before-blocks": "error"
diff --git a/.travis.yml b/.travis.yml
index 4756ac35..826217cd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,10 +2,9 @@ language: node_js
sudo: required
dist: trusty
node_js:
- - "4"
- "8"
+ - "12"
services:
- - mysql
- docker
before_script:
- npm run lint
diff --git a/README.md b/README.md
index def570d5..9e3f70e4 100644
--- a/README.md
+++ b/README.md
@@ -3,14 +3,18 @@ A MySQL binlog listener running on Node.js.
ZongJi (踪迹) is pronounced as `zōng jì` in Chinese.
-This package is a "pure JS" implementation based on [`node-mysql`](https://github.com/felixge/node-mysql). Since v0.2.0, The native part (which was written in C++) has been dropped.
+This package is a pure JS implementation based on [`mysql`](https://github.com/mysqljs/mysql). It has been tested to work in MySQL 5.5, 5.6, and 5.7.
-This package has been tested to work in MySQL 5.5, 5.6, and 5.7.
+# Latest Release
+
+The latest release is v0.5.0, only supports Node.js from v8.
+
+v0.4.7 is the last release which supports Node.js v4.x.
## Quick Start
```javascript
-var zongji = new ZongJi({ /* ... MySQL Connection Settings ... */ });
+let zongji = new ZongJi({ /* ... MySQL Connection Settings ... */ });
// Each change to the replication log results in an event
zongji.on('binlog', function(evt) {
@@ -27,7 +31,7 @@ For a complete implementation see [`example.js`](example.js)...
## Installation
-* Requires Node.js v4+
+* Requires Node.js v8+
```bash
$ npm install zongji
@@ -59,12 +63,12 @@ For a complete implementation see [`example.js`](example.js)...
The `ZongJi` constructor accepts one argument of either:
-* An object containing MySQL connection details in the same format as used by `node-mysql`
-* Or, a `node-mysql` `Connection` or `Pool` object that will be used for querying column information.
+* An object containing MySQL connection details in the same format as used by [package mysql](https://npm.im/mysql)
+* Or, a [mysql](https://npm.im/mysql) `Connection` or `Pool` object that will be used for querying column information.
If a `Connection` or `Pool` object is passed to the constructor, it will not be destroyed/ended by Zongji's `stop()` method.
-If there is a `dateStrings` `node-mysql` configuration option in the connection details or connection, `ZongJi` will follow it.
+If there is a `dateStrings` `mysql` configuration option in the connection details or connection, `ZongJi` will follow it.
Each instance includes the following methods:
@@ -72,17 +76,25 @@ Method Name | Arguments | Description
------------|-----------|------------------------
`start` | `options` | Start receiving replication events, see options listed below
`stop` | *None* | Disconnect from MySQL server, stop receiving events
-`set` | `options` | Change options after `start()`
`on` | `eventName`, `handler` | Add a listener to the `binlog` or `error` event. Each handler function accepts one argument.
+Some events can be emitted in different phases:
+
+Event Name | Description
+-----------|------------------------
+`ready` | This event is occurred right after ZongJi successfully established a connection, setup slave status, and set binlog position.
+`binlog` | Once a binlog is received and passes the filter, it will bubble up with this event.
+`error` | Every error will be caught by this event.
+`stopped` | Emitted when ZongJi connection is stopped (ZongJi#stop is called).
+
**Options available:**
Option Name | Type | Description
------------|------|-------------------------------
`serverId` | `integer` | [Unique number (1 - 232)](http://dev.mysql.com/doc/refman/5.0/en/replication-options.html#option_mysqld_server-id) to identify this replication slave instance. Must be specified if running more than one instance of ZongJi. Must be used in `start()` method for effect.
**Default:** `1`
`startAtEnd` | `boolean` | Pass `true` to only emit binlog events that occur after ZongJi's instantiation. Must be used in `start()` method for effect.
**Default:** `false`
-`binlogName` | `string` | Begin reading events from this binlog file. If specified together with `binlogNextPos`, will take precedence over `startAtEnd`.
-`binlogNextPos` | `integer` | Begin reading events from this position. Must be included with `binlogName`.
+`filename` | `string` | Begin reading events from this binlog file. If specified together with `position`, will take precedence over `startAtEnd`.
+`position` | `integer` | Begin reading events from this position. Must be included with `filename`.
`includeEvents` | `[string]` | Array of event names to include
**Example:** `['writerows', 'updaterows', 'deleterows']`
`excludeEvents` | `[string]` | Array of event names to exclude
**Example:** `['rotate', 'tablemap']`
`includeSchema` | `object` | Object describing which databases and tables to include (Only for row events). Use database names as the key and pass an array of table names or `true` (for the entire database).
**Example:** ```{ 'my_database': ['allow_table', 'another_table'], 'another_db': true }```
@@ -98,7 +110,7 @@ Event name | Description
`unknown` | Catch any other events
`query` | [Insert/Update/Delete Query](http://dev.mysql.com/doc/internals/en/query-event.html)
`intvar` | [Autoincrement and LAST_INSERT_ID](https://dev.mysql.com/doc/internals/en/intvar-event.html)
-`rotate` | [New Binlog file](http://dev.mysql.com/doc/internals/en/rotate-event.html) Not required to be included to rotate to new files, but it is required to be included in order to keep the `binlogName` and `binlogNextPos` properties updated with current values for [graceful restarting on errors](https://gist.github.com/numtel/5b37b2a7f47b380c1a099596c6f3db2f).
+`rotate` | [New Binlog file](http://dev.mysql.com/doc/internals/en/rotate-event.html) Not required to be included to rotate to new files, but it is required to be included in order to keep the `filename` and `position` properties updated with current values for [graceful restarting on errors](https://gist.github.com/numtel/5b37b2a7f47b380c1a099596c6f3db2f).
`format` | [Format Description](http://dev.mysql.com/doc/internals/en/format-description-event.html)
`xid` | [Transaction ID](http://dev.mysql.com/doc/internals/en/xid-event.html)
`tablemap` | Before any row event (must be included for any other row events)
@@ -117,8 +129,8 @@ Name | Description
## Important Notes
-* :star2: [All types allowed by `node-mysql`](https://github.com/felixge/node-mysql#type-casting) are supported by this package.
-* :speak_no_evil: While 64-bit integers in MySQL (`BIGINT` type) allow values in the range of 264 (± ½ × 264 for signed values), Javascript's internal storage of numbers limits values to 253, making the allowed range of `BIGINT` fields only `-9007199254740992` to `9007199254740992`. Unsigned 64-bit integers must also not exceed `9007199254740992`.
+* :star2: [All types allowed by `mysql`](https://github.com/mysqljs/mysql#type-casting) are supported by this package.
+* :speak_no_evil: 64-bit integer is supported via package big-integer(see #108). If an integer is within the safe range of JS number (-2^53, 2^53), a Number object will returned, otherwise, will return as String.
* :point_right: `TRUNCATE` statement does not cause corresponding `DeleteRows` event. Use unqualified `DELETE FROM` for same effect.
* When using fractional seconds with `DATETIME` and `TIMESTAMP` data types in MySQL > 5.6.4, only millisecond precision is available due to the limit of Javascript's `Date` object.
@@ -131,7 +143,7 @@ Name | Description
I learnt many things from following resources while making ZongJi.
-* https://github.com/felixge/node-mysql
+* https://github.com/mysqljs/mysql
* https://github.com/felixge/faster-than-c/
* http://intuitive-search.blogspot.co.uk/2011/07/binary-log-api-and-replication-listener.html
* https://github.com/Sannis/node-mysql-libmysqlclient
diff --git a/docker-test.sh b/docker-test.sh
index 0c4b3cbd..3b21fc5f 100755
--- a/docker-test.sh
+++ b/docker-test.sh
@@ -2,6 +2,10 @@
MYSQL_HOSTS="mysql55 mysql56 mysql57"
for hostname in ${MYSQL_HOSTS}; do
- echo $hostname
+ echo $hostname + node 8
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:8 npm test
+ echo $hostname + node 10
+ docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:10 npm test
+ echo $hostname + node 12
+ docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:12 npm test
done
diff --git a/example.js b/example.js
index 636dfcf6..361e11f5 100644
--- a/example.js
+++ b/example.js
@@ -1,7 +1,7 @@
// Client code
-var ZongJi = require('./');
+const ZongJi = require('./');
-var zongji = new ZongJi({
+const zongji = new ZongJi({
host : 'localhost',
user : 'zongji',
password : 'zongji',
diff --git a/index.js b/index.js
index 16ab104e..db6746f9 100644
--- a/index.js
+++ b/index.js
@@ -1,210 +1,139 @@
-var mysql = require('mysql');
-var Connection = require('mysql/lib/Connection');
-var Pool = require('mysql/lib/Pool');
-
-var util = require('util');
-var EventEmitter = require('events').EventEmitter;
-var generateBinlog = require('./lib/sequence/binlog');
-
-var alternateDsn = [
- { type: Connection, config: function(obj) { return obj.config; } },
- { type: Pool, config: function(obj) { return obj.config.connectionConfig; } }
-];
+const mysql = require('mysql');
+const util = require('util');
+const EventEmitter = require('events').EventEmitter;
+const initBinlogClass = require('./lib/sequence/binlog');
+
+const ConnectionConfigMap = {
+ 'Connection': obj => obj.config,
+ 'Pool': obj => obj.config.connectionConfig,
+};
-function ZongJi(dsn, options) {
- this.set(options);
+const TableInfoQueryTemplate = 'SELECT ' +
+ 'COLUMN_NAME, COLLATION_NAME, CHARACTER_SET_NAME, ' +
+ 'COLUMN_COMMENT, COLUMN_TYPE ' +
+ 'FROM information_schema.columns ' + "WHERE table_schema='%s' AND table_name='%s'";
+function ZongJi(dsn) {
EventEmitter.call(this);
- var binlogDsn;
-
- // one connection to send table info query
- // Check first argument against possible connection objects
- for (var i = 0; i < alternateDsn.length; i++) {
- if (dsn instanceof alternateDsn[i].type) {
- this.ctrlConnection = dsn;
- this.ctrlConnectionOwner = false;
- binlogDsn = cloneObjectSimple(alternateDsn[i].config(dsn));
- }
- }
-
- if (!binlogDsn) {
- // assuming that the object passed is the connection settings
- var ctrlDsn = cloneObjectSimple(dsn);
- this.ctrlConnection = mysql.createConnection(ctrlDsn);
- this.ctrlConnection.on('error', this._emitError.bind(this));
- this.ctrlConnection.on('unhandledError', this._emitError.bind(this));
- this.ctrlConnection.connect();
- this.ctrlConnectionOwner = true;
-
- binlogDsn = dsn;
- }
+ this._options({});
+ this._filters({});
this.ctrlCallbacks = [];
-
- this.connection = mysql.createConnection(binlogDsn);
- this.connection.on('error', this._emitError.bind(this));
- this.connection.on('unhandledError', this._emitError.bind(this));
-
this.tableMap = {};
this.ready = false;
this.useChecksum = false;
- // Include 'rotate' events to keep these properties updated
- this.binlogName = null;
- this.binlogNextPos = null;
- this._init();
+ this._establishConnection(dsn);
}
-var cloneObjectSimple = function(obj) {
- var out = {};
- for (var i in obj) {
- if (obj.hasOwnProperty(i)) {
- out[i] = obj[i];
- }
- }
- return out;
-};
-
util.inherits(ZongJi, EventEmitter);
-ZongJi.prototype._init = function() {
- var self = this;
- var binlogOptions = {
- tableMap: self.tableMap,
+// dsn - can be one instance of Connection or Pool / object / url string
+ZongJi.prototype._establishConnection = function(dsn) {
+ const createConnection = (options) => {
+ let connection = mysql.createConnection(options);
+ connection.on('error', this.emit.bind(this, 'error'));
+ connection.on('unhandledError', this.emit.bind(this, 'error'));
+ // don't need to call connection.connect() here
+ // we use implicitly established connection
+ // see https://github.com/mysqljs/mysql#establishing-connections
+ return connection;
};
- var asyncMethods = [
- {
- name: '_isChecksumEnabled',
- callback: function(checksumEnabled) {
- self.useChecksum = checksumEnabled;
- binlogOptions.useChecksum = checksumEnabled;
- }
- },
- {
- name: '_findBinlogEnd',
- callback: function(result) {
- if (result && self.options.startAtEnd) {
- binlogOptions.filename = result.Log_name;
- binlogOptions.position = result.File_size;
- }
- }
- }
- ];
-
- var methodIndex = 0;
- var nextMethod = function() {
- var method = asyncMethods[methodIndex];
- self[method.name](function(/* args */) {
- method.callback.apply(this, arguments);
- methodIndex++;
- if (methodIndex < asyncMethods.length) {
- nextMethod();
- }
- else {
- ready();
- }
- });
- };
- nextMethod();
+ const configFunc = ConnectionConfigMap[dsn.constructor.name];
+ let binlogDsn;
- var ready = function() {
- // Run asynchronously from _init(), as serverId option set in start()
- if (self.options.serverId !== undefined) {
- binlogOptions.serverId = self.options.serverId;
- }
+ if (typeof dsn === 'object' && configFunc) {
+ // dsn is a pool or connection object
+ let conn = dsn; // reuse as ctrlConnection
+ this.ctrlConnection = conn;
+ this.ctrlConnectionOwner = false;
+ binlogDsn = Object.assign({}, configFunc(conn));
+ }
- if (('binlogName' in self.options) && ('binlogNextPos' in self.options)) {
- binlogOptions.filename = self.options.binlogName;
- binlogOptions.position = self.options.binlogNextPos;
- }
+ if (!binlogDsn) {
+ // assuming that the object passed is the connection settings
+ this.ctrlConnectionOwner = true;
+ this.ctrlConnection = createConnection(dsn);
+ binlogDsn = dsn;
+ }
- self.binlog = generateBinlog.call(self, binlogOptions);
- self.ready = true;
- self._executeCtrlCallbacks();
- };
+ this.connection = createConnection(binlogDsn);
};
ZongJi.prototype._isChecksumEnabled = function(next) {
- var self = this;
- var sql = 'select @@GLOBAL.binlog_checksum as checksum';
- var ctrlConnection = self.ctrlConnection;
- var connection = self.connection;
-
- ctrlConnection.query(sql, function(err, rows) {
- if (err) {
- if (err.toString().match(/ER_UNKNOWN_SYSTEM_VARIABLE/)) {
- // MySQL < 5.6.2 does not support @@GLOBAL.binlog_checksum
- return next(false);
- } else {
- // Any other errors should be emitted
- self.emit('error', err);
- return;
+ const SelectChecksumParamSql = 'select @@GLOBAL.binlog_checksum as checksum';
+ const SetChecksumSql = 'set @master_binlog_checksum=@@global.binlog_checksum';
+
+ const query = (conn, sql) => {
+ return new Promise(
+ (resolve, reject) => {
+ conn.query(sql, (err, result) => {
+ if (err) {
+ reject(err);
+ }
+ else {
+ resolve(result);
+ }
+ });
}
- }
+ );
+ };
- var checksumEnabled = true;
- if (rows[0].checksum === 'NONE') {
- checksumEnabled = false;
- }
+ let checksumEnabled = true;
- var setChecksumSql = 'set @master_binlog_checksum=@@global.binlog_checksum';
- if (checksumEnabled) {
- connection.query(setChecksumSql, function(err) {
- if (err) {
- // Errors should be emitted
- self.emit('error', err);
- return;
- }
- next(checksumEnabled);
- });
- } else {
- next(checksumEnabled);
- }
- });
+ query(this.ctrlConnection, SelectChecksumParamSql)
+ .then(rows => {
+ if (rows[0].checksum === 'NONE') {
+ checksumEnabled = false;
+ }
+
+ if (checksumEnabled) {
+ return query(this.connection, SetChecksumSql);
+ }
+ })
+ .catch(err => {
+ if (err.toString().match(/ER_UNKNOWN_SYSTEM_VARIABLE/)) {
+ checksumEnabled = false;
+ // a simple query to open this.connection
+ return query(this.connection, 'SELECT 1');
+ }
+ else {
+ next(err);
+ }
+ })
+ .then(() => {
+ next(null, checksumEnabled);
+ });
};
ZongJi.prototype._findBinlogEnd = function(next) {
- var self = this;
- self.ctrlConnection.query('SHOW BINARY LOGS', function(err, rows) {
+ this.ctrlConnection.query('SHOW BINARY LOGS', (err, rows) => {
if (err) {
// Errors should be emitted
- self.emit('error', err);
- return;
+ next(err);
+ }
+ else {
+ next(null, rows.length > 0 ? rows[rows.length - 1] : null);
}
- next(rows.length > 0 ? rows[rows.length - 1] : null);
});
};
-ZongJi.prototype._executeCtrlCallbacks = function() {
- if (this.ctrlCallbacks.length > 0) {
- this.ctrlCallbacks.forEach(function(cb) {
- setImmediate(cb);
- });
- }
-};
-
-var tableInfoQueryTemplate = 'SELECT ' +
- 'COLUMN_NAME, COLLATION_NAME, CHARACTER_SET_NAME, ' +
- 'COLUMN_COMMENT, COLUMN_TYPE ' +
- 'FROM information_schema.columns ' + "WHERE table_schema='%s' AND table_name='%s'";
-
ZongJi.prototype._fetchTableInfo = function(tableMapEvent, next) {
- var self = this;
- var sql = util.format(tableInfoQueryTemplate,
+ const sql = util.format(TableInfoQueryTemplate,
tableMapEvent.schemaName, tableMapEvent.tableName);
- this.ctrlConnection.query(sql, function(err, rows) {
+ this.ctrlConnection.query(sql, (err, rows) => {
if (err) {
// Errors should be emitted
- self.emit('error', err);
+ this.emit('error', err);
// This is a fatal error, no additional binlog events will be
// processed since next() will never be called
return;
}
if (rows.length === 0) {
- self.emit('error', new Error(
+ this.emit('error', new Error(
'Insufficient permissions to access: ' +
tableMapEvent.schemaName + '.' + tableMapEvent.tableName));
// This is a fatal error, no additional binlog events will be
@@ -212,7 +141,7 @@ ZongJi.prototype._fetchTableInfo = function(tableMapEvent, next) {
return;
}
- self.tableMap[tableMapEvent.tableId] = {
+ this.tableMap[tableMapEvent.tableId] = {
columnSchemas: rows,
parentSchema: tableMapEvent.schemaName,
tableName: tableMapEvent.tableName
@@ -222,97 +151,203 @@ ZongJi.prototype._fetchTableInfo = function(tableMapEvent, next) {
});
};
-ZongJi.prototype.set = function(options) {
- this.options = options || {};
+// #_options will reset all the options.
+ZongJi.prototype._options = function({
+ serverId,
+ filename,
+ position,
+ startAtEnd,
+}) {
+ this.options = {
+ serverId,
+ filename,
+ position,
+ startAtEnd,
+ };
};
-ZongJi.prototype.start = function(options) {
- var self = this;
- self.set(options);
-
- var _start = function() {
- self.connection._implyConnect();
- self.connection._protocol._enqueue(new self.binlog(function(error, event) {
- if (error) return self.emit('error', error);
- // Do not emit events that have been filtered out
- if (event === undefined || event._filtered === true) return;
-
- switch (event.getTypeName()) {
- case 'TableMap':
- var tableMap = self.tableMap[event.tableId];
-
- if (!tableMap) {
- self.connection.pause();
- self._fetchTableInfo(event, function() {
- // merge the column info with metadata
- event.updateColumnInfo();
- self.emit('binlog', event);
- self.connection.resume();
- });
- return;
- }
- break;
- case 'Rotate':
- if (self.binlogName !== event.binlogName) {
- self.binlogName = event.binlogName;
- }
- break;
- }
- self.binlogNextPos = event.nextPosition;
- self.emit('binlog', event);
- }));
+// #_filters will reset all the filters.
+ZongJi.prototype._filters = function({
+ includeEvents,
+ excludeEvents,
+ includeSchema,
+ excludeSchema,
+}) {
+ this.filters = {
+ includeEvents,
+ excludeEvents,
+ includeSchema,
+ excludeSchema,
};
+};
- if (this.ready) {
- _start();
+ZongJi.prototype.get = function(name) {
+ let result;
+ if (typeof name === 'string') {
+ result = this.options[name];
}
- else {
- this.ctrlCallbacks.push(_start);
+ else if (Array.isArray(name)) {
+ result = name.reduce(
+ (acc, cur) => {
+ acc[cur] = this.options[cur];
+ return acc;
+ },
+ {}
+ );
+ }
+
+ return result;
+};
+
+// @options contains a list options
+// - `serverId` unique identifier
+// - `filename`, `position` the position of binlog to beigin with
+// - `startAtEnd` if true, will update filename / postion automatically
+// - `includeEvents`, `excludeEvents`, `includeSchema`, `exludeSchema` filter different binlog events bubbling
+ZongJi.prototype.start = function(options = {}) {
+
+ this._options(options);
+ this._filters(options);
+
+ const testChecksum = (resolve, reject) => {
+ this._isChecksumEnabled((err, checksumEnabled) => {
+ if (err) {
+ reject(err);
+ }
+ else {
+ this.useChecksum = checksumEnabled;
+ resolve();
+ }
+ });
+ };
+
+
+ const findBinlogEnd = (resolve, reject) => {
+ this._findBinlogEnd((err, result) => {
+ if (err) {
+ return reject(err);
+ }
+
+ if (result) {
+ this._options(
+ Object.assign({}, options, {
+ filename: result.Log_name,
+ position: result.File_size,
+ })
+ );
+ }
+
+ resolve();
+ });
+ };
+
+ const binlogHandler = (error, event) => {
+ if (error) {
+ return this.emit('error', error);
+ }
+
+ // Do not emit events that have been filtered out
+ if (event === undefined || event._filtered === true) return;
+
+ switch (event.getTypeName()) {
+ case 'TableMap': {
+ const tableMap = this.tableMap[event.tableId];
+ if (!tableMap) {
+ this.connection.pause();
+ this._fetchTableInfo(event, () => {
+ // merge the column info with metadata
+ event.updateColumnInfo();
+ this.emit('binlog', event);
+ this.connection.resume();
+ });
+ return;
+ }
+ break;
+ }
+ case 'Rotate':
+ if (this.options.filename !== event.binlogName) {
+ this.options.filename = event.binlogName;
+ }
+ break;
+ }
+ this.options.position = event.nextPosition;
+ this.emit('binlog', event);
+ };
+
+ let promises = [new Promise(testChecksum)];
+
+ if (this.options.startAtEnd) {
+ promises.push(new Promise(findBinlogEnd));
}
+
+ Promise.all(promises)
+ .then(() => {
+ this.BinlogClass = initBinlogClass(this);
+ this.ready = true;
+ this.emit('ready');
+
+ this.connection._protocol._enqueue(
+ new this.BinlogClass(binlogHandler)
+ );
+ })
+ .catch(err => {
+ this.emit('error', err);
+ });
+
};
ZongJi.prototype.stop = function() {
- var self = this;
// Binary log connection does not end with destroy()
- self.connection.destroy();
- self.ctrlConnection.query(
- 'KILL ' + self.connection.threadId,
- function() {
- if (self.ctrlConnectionOwner)
- self.ctrlConnection.destroy();
+ this.connection.destroy();
+ this.ctrlConnection.query(
+ 'KILL ' + this.connection.threadId,
+ () => {
+ if (this.ctrlConnectionOwner) {
+ this.ctrlConnection.destroy();
+ }
+ this.emit('stopped');
}
);
};
-ZongJi.prototype._skipEvent = function(eventName) {
- var include = this.options.includeEvents;
- var exclude = this.options.excludeEvents;
- return !(
- (include === undefined ||
- (include instanceof Array && include.indexOf(eventName) !== -1)) &&
- (exclude === undefined ||
- (exclude instanceof Array && exclude.indexOf(eventName) === -1)));
-};
+// It includes every events by default.
+ZongJi.prototype._skipEvent = function(name) {
+ const includes = this.filters.includeEvents;
+ const excludes = this.filters.excludeEvents;
-ZongJi.prototype._skipSchema = function(database, table) {
- var include = this.options.includeSchema;
- var exclude = this.options.excludeSchema;
- return !(
- (include === undefined ||
- (database !== undefined && (database in include) &&
- (include[database] === true ||
- (include[database] instanceof Array &&
- include[database].indexOf(table) !== -1)))) &&
- (exclude === undefined ||
- (database !== undefined &&
- (!(database in exclude) ||
- (exclude[database] !== true &&
- (exclude[database] instanceof Array &&
- exclude[database].indexOf(table) === -1))))));
+ let included = (includes === undefined) ||
+ (Array.isArray(includes) && (includes.indexOf(name) > -1));
+ let excluded = Array.isArray(excludes) && (excludes.indexOf(name) > -1);
+
+ return excluded || !included;
};
-ZongJi.prototype._emitError = function(error) {
- this.emit('error', error);
+// It doesn't skip any schema by default.
+ZongJi.prototype._skipSchema = function(database, table) {
+ const includes = this.filters.includeSchema;
+ const excludes = this.filters.excludeSchema || {};
+
+ let included = (includes === undefined) ||
+ (
+ (database in includes) &&
+ (
+ includes[database] === true ||
+ (
+ Array.isArray(includes[database]) &&
+ includes[database].indexOf(table) > -1
+ )
+ )
+ );
+ let excluded = (database in excludes) &&
+ (
+ excludes[database] === true ||
+ (
+ Array.isArray(excludes[database]) &&
+ excludes[database].indexOf(table) > -1
+ )
+ );
+
+ return excluded || !included;
};
module.exports = ZongJi;
diff --git a/lib/binlog_event.js b/lib/binlog_event.js
index 395dfcb2..1a905219 100644
--- a/lib/binlog_event.js
+++ b/lib/binlog_event.js
@@ -1,6 +1,8 @@
-var util = require('util');
-var Common = require('./common');
+const util = require('util');
+const Common = require('./common');
+//TODO get rid parser from binlog event class
+// probably a factory to create them
function BinlogEvent(parser, options) {
this.timestamp = options.timestamp;
this.nextPosition = options.nextPosition;
@@ -118,7 +120,7 @@ function IntVar(parser) {
}
util.inherits(IntVar, BinlogEvent);
-var INTVAR_TYPES = ['INVALID_INT', 'LAST_INSERT_ID', 'INSERT_ID'];
+const INTVAR_TYPES = ['INVALID_INT', 'LAST_INSERT_ID', 'INSERT_ID'];
IntVar.prototype.getIntTypeName = function() {
return INTVAR_TYPES[this.type] || 'INVALID_INT';
};
@@ -141,18 +143,18 @@ IntVar.prototype.dump = function() {
function TableMap(parser, options, zongji) {
BinlogEvent.apply(this, arguments);
- this.tableMap = options.tableMap;
+ this.tableMap = zongji.tableMap;
// post-header
this._readTableId(parser);
this.flags = parser.parseUnsignedNumber(2);
// payload
- var schemaNameLength = parser.parseUnsignedNumber(1);
+ const schemaNameLength = parser.parseUnsignedNumber(1);
this.schemaName = parser.parseString(schemaNameLength);
parser.parseUnsignedNumber(1);
- var tableNameLength = parser.parseUnsignedNumber(1);
+ const tableNameLength = parser.parseUnsignedNumber(1);
this.tableName = parser.parseString(tableNameLength);
if (zongji._skipSchema(this.schemaName, this.tableName)) {
@@ -177,18 +179,18 @@ function TableMap(parser, options, zongji) {
util.inherits(TableMap, BinlogEvent);
TableMap.prototype.updateColumnInfo = function() {
- var columnsMetadata = this.columnsMetadata;
- for (var i = 0; i < this.columnCount; i++) {
+ const columnsMetadata = this.columnsMetadata;
+ for (let i = 0; i < this.columnCount; i++) {
if (columnsMetadata[i] && columnsMetadata[i].type) {
this.columnTypes[i] = columnsMetadata[i].type;
delete columnsMetadata[i].type;
}
}
- var tableMap = this.tableMap[this.tableId];
+ const tableMap = this.tableMap[this.tableId];
- var columnSchemas = tableMap.columnSchemas;
- var columns = [];
- for (var j = 0; j < this.columnCount; j++) {
+ const columnSchemas = tableMap.columnSchemas;
+ const columns = [];
+ for (let j = 0; j < this.columnCount; j++) {
columns.push({
name: columnSchemas[j].COLUMN_NAME,
charset: columnSchemas[j].CHARACTER_SET_NAME,
@@ -203,7 +205,7 @@ TableMap.prototype.updateColumnInfo = function() {
TableMap.prototype._readColumnMetadata = function(parser) {
this.columnsMetadata = this.columnTypes.map(function(code) {
- var result;
+ let result;
switch (code) {
case Common.MysqlTypes.FLOAT:
@@ -217,13 +219,14 @@ TableMap.prototype._readColumnMetadata = function(parser) {
'max_length': parser.parseUnsignedNumber(2)
};
break;
- case Common.MysqlTypes.BIT:
- var bits = parser.parseUnsignedNumber(1);
- var bytes = parser.parseUnsignedNumber(1);
+ case Common.MysqlTypes.BIT: {
+ const bits = parser.parseUnsignedNumber(1);
+ const bytes = parser.parseUnsignedNumber(1);
result = {
bits: bytes * 8 + bits
};
break;
+ }
case Common.MysqlTypes.NEWDECIMAL:
result = {
precision: parser.parseUnsignedNumber(1),
@@ -238,14 +241,14 @@ TableMap.prototype._readColumnMetadata = function(parser) {
};
break;
case Common.MysqlTypes.STRING:
- case Common.MysqlTypes.VAR_STRING:
+ case Common.MysqlTypes.VAR_STRING: {
// The STRING type sets a 'real_type' field to indicate the
// actual type which is fundamentally incompatible with STRING
// parsing. Setting a 'type' key in this hash will cause
// TableMap event to override the main field 'type' with the
// provided 'type' here.
- var metadata = (parser.parseUnsignedNumber(1) << 8) + parser.parseUnsignedNumber(1);
- var realType = metadata >> 8;
+ const metadata = (parser.parseUnsignedNumber(1) << 8) + parser.parseUnsignedNumber(1);
+ const realType = metadata >> 8;
if (realType === Common.MysqlTypes.ENUM
|| realType === Common.MysqlTypes.SET) {
result = {
@@ -259,6 +262,7 @@ TableMap.prototype._readColumnMetadata = function(parser) {
};
}
break;
+ }
case Common.MysqlTypes.TIMESTAMP2:
case Common.MysqlTypes.DATETIME2:
case Common.MysqlTypes.TIME2:
diff --git a/lib/capture.js b/lib/capture.js
deleted file mode 100644
index 37c16186..00000000
--- a/lib/capture.js
+++ /dev/null
@@ -1,43 +0,0 @@
-var mysql = require('mysql');
-
-var query = mysql.createQuery('select 1', function() {});
-
-var Query = query.constructor;
-// `super_` is set by calling util.inherits
-// see http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor
-var Sequence = Query.super_;
-
-var QueryPrototype = Object.getPrototypeOf(query);
-var SequencePrototype = Object.getPrototypeOf(QueryPrototype);
-
-var classes = {
- Query: Query,
- Sequence: Sequence,
- QueryPrototype: QueryPrototype,
- SequencePrototype: SequencePrototype
-};
-
-var captured = false;
-
-
-var captureIt = function(connection) {
- var ConnectionPrototype = Object.getPrototypeOf(connection);
- ConnectionPrototype._patch = function() {
- classes.ProtocolPrototype = Object.getPrototypeOf(this._protocol);
- classes.ConnectionPrototype = ConnectionPrototype;
- classes.Connection = connection.constructor;
- classes.Protocol = this._protocol.constructor;
- captured = true;
- };
-
- connection._patch();
- delete ConnectionPrototype._patch;
-};
-
-// connection {Connection} an instance created by mysql.createConnection
-module.exports = function(connection) {
- if (!captured) {
- captureIt(connection);
- }
- return classes;
-};
diff --git a/lib/code_map.js b/lib/code_map.js
index ac80d60b..1e66b884 100644
--- a/lib/code_map.js
+++ b/lib/code_map.js
@@ -1,7 +1,7 @@
-var events = require('./binlog_event');
-var rowsEvents = require('./rows_event');
+const events = require('./binlog_event');
+const rowsEvents = require('./rows_event');
-var CodeEvent = [
+const CodeEvent = [
'UNKNOWN_EVENT',
'START_EVENT_V3',
'QUERY_EVENT',
@@ -40,7 +40,7 @@ var CodeEvent = [
'PREVIOUS_GTIDS_LOG_EVENT'
];
-var EventClass = {
+const EventClass = {
UNKNOWN_EVENT: events.Unknown,
QUERY_EVENT: events.Query,
INTVAR_EVENT: events.IntVar,
diff --git a/lib/common.js b/lib/common.js
index 355cdae1..e4bdc98f 100644
--- a/lib/common.js
+++ b/lib/common.js
@@ -1,8 +1,9 @@
-var iconv = require('iconv-lite');
-var decodeJson = require('./json_decode');
-var dtDecode = require('./datetime_decode');
+const iconv = require('iconv-lite');
+const decodeJson = require('./json_decode');
+const dtDecode = require('./datetime_decode');
+const bigInt = require('big-integer');
-var MysqlTypes = exports.MysqlTypes = {
+const MysqlTypes = exports.MysqlTypes = {
DECIMAL: 0,
TINY: 1,
SHORT: 2,
@@ -38,34 +39,37 @@ var MysqlTypes = exports.MysqlTypes = {
GEOMETRY: 255,
};
-exports.parseUInt64 = function(parser) {
- var low = parser.parseUnsignedNumber(4);
- var high = parser.parseUnsignedNumber(4);
-
- if (this) {
- // Pass extra output to context
- this.low = low;
- this.high = high;
+const TWO_TO_POWER_THIRTY_TWO = Math.pow(2, 32);
+const TWO_TO_POWER_SIXTY_THREE = '9223372036854775808'; // Math.pow(2, 63) or 1 << 63
+// This function will return a Number
+// if the reuslt < Math.MAX_SAFE_INTEGER or reuslt > Math.MIN_SAFE_INTEGER,
+// otherwise, will return a string.
+const parseUInt64 = exports.parseUInt64 = function(parser) {
+ const low = parser.parseUnsignedNumber(4);
+ const high = parser.parseUnsignedNumber(4);
+
+ if (high >>> 21) { // using bigint here
+ return bigInt(TWO_TO_POWER_THIRTY_TWO).multiply(high).add(low).toString();
}
return (high * Math.pow(2,32)) + low;
};
exports.parseUInt48 = function(parser) {
- var low = parser.parseUnsignedNumber(4);
- var high = parser.parseUnsignedNumber(2);
+ const low = parser.parseUnsignedNumber(4);
+ const high = parser.parseUnsignedNumber(2);
return (high * Math.pow(2, 32)) + low;
};
-exports.parseUInt24 = function(parser) {
- var low = parser.parseUnsignedNumber(2);
- var high = parser.parseUnsignedNumber(1);
+const parseUInt24 = exports.parseUInt24 = function(parser) {
+ const low = parser.parseUnsignedNumber(2);
+ const high = parser.parseUnsignedNumber(1);
return (high << 16) + low;
};
exports.parseBytesArray = function(parser, length) {
- var result = new Array(length);
- for (var i = 0; i < length; i++) {
+ const result = new Array(length);
+ for (let i = 0; i < length; i++) {
result[i] = parser.parseUnsignedNumber(1);
}
return result;
@@ -75,7 +79,7 @@ exports.parseBytesArray = function(parser, length) {
// @param type String Definition of column 'set(...)' or 'enum(...)'
// @param prefixLen Integer Number of characters before list starts
// (e.g. 'set(': 4, 'enum(': 5)
-var parseSetEnumTypeDef = function(type, prefixLen) {
+const parseSetEnumTypeDef = function(type, prefixLen) {
// listed distinct elements should not include commas
return type.substr(prefixLen, type.length - prefixLen - 1)
.split(',').map(function(opt) {
@@ -84,67 +88,41 @@ var parseSetEnumTypeDef = function(type, prefixLen) {
});
};
-var zeroPad = exports.zeroPad = function(num, size) {
+const zeroPad = exports.zeroPad = function(num, size) {
// Max 32 digits
- var s = '00000000000000000000000000000000' + num;
+ const s = '00000000000000000000000000000000' + num;
return s.substr(s.length-size);
};
-var sliceBits = exports.sliceBits = function(input, start, end) {
+const sliceBits = exports.sliceBits = function(input, start, end) {
// ex: start: 10, end: 15 = "111110000000000"
- var match = (((1 << end) - 1) ^ ((1 << start) - 1));
+ const match = (((1 << end) - 1) ^ ((1 << start) - 1));
return (input & match) >> start;
};
-// See information about IEEE-754 Floating point numbers:
-// http://www.h-schmidt.net/FloatConverter/IEEE754.html
-// http://babbage.cs.qc.cuny.edu/IEEE-754.old/64bit.html
+// Use Typed Arrays to convert IEEE 754 numbers
// Pass only high for 32-bit float, pass high and low for 64-bit double
-var parseIEEE754Float = exports.parseIEEE754Float = function(high, low) {
- var lastSignificantBit, sigFigs, expLeading;
+const parseIEEE754Float = exports.parseIEEE754Float = function(high, low) {
if (low !== undefined) {
- // 64-bit: 1 sign, 11 exponent, 52 significand
- lastSignificantBit = 20;
- sigFigs = 52;
- expLeading = 1023; // 2^(11-1) - 1
+ let value = new DataView(new ArrayBuffer(8));
+ value.setUint32(0, high);
+ value.setUint32(4, low);
+ return value.getFloat64(0);
} else {
- // 32-bit: 1 sign, 8 exponent, 23 significand
- lastSignificantBit = 23;
- sigFigs = 23;
- expLeading = 127; // 2^(8-1) - 1
- }
-
- var sign = (high & (1 << 31)) !== 0 ? -1 : 1;
- var exponent = sliceBits(high, lastSignificantBit, 31) - expLeading;
- var significandBits = sliceBits(high, 0, lastSignificantBit);
- var significand = 1; // Becomes value between 1, 2
-
- for (var i = 0; i < lastSignificantBit; i++) {
- if (significandBits & (1 << i)) {
- significand += 1 / (1 << (sigFigs - i));
- }
- }
-
- if (low !== undefined) {
- for (var j = 0; j < 32; j++) {
- if (low & (1 << j)) {
- // Bitwise operators only work on up to 32 bits
- significand += 1 / Math.pow(2, sigFigs - j);
- }
- }
+ let value = new DataView(new ArrayBuffer(4));
+ value.setUint32(0, high);
+ return value.getFloat32(0);
}
-
- return sign * Math.pow(2, exponent) * significand;
};
-var getUInt32Value = exports.getUInt32Value = function(input) {
+const getUInt32Value = exports.getUInt32Value = function(input) {
// Last bit is not sign, it is part of value!
if (input & (1 << 31)) return Math.pow(2, 31) + (input & ((1 << 31) -1));
else return input;
};
-var parseAnyInt = function(parser, column, columnSchema) {
- var result, int64, size;
+const parseAnyInt = function(parser, column, columnSchema) {
+ let result, size;
switch (column.type) {
case MysqlTypes.TINY:
size = 1;
@@ -156,7 +134,7 @@ var parseAnyInt = function(parser, column, columnSchema) {
break;
case MysqlTypes.INT24:
size = 3;
- result = exports.parseUInt24(parser);
+ result = parseUInt24(parser);
break;
case MysqlTypes.LONG:
size = 4;
@@ -164,33 +142,37 @@ var parseAnyInt = function(parser, column, columnSchema) {
break;
case MysqlTypes.LONGLONG:
size = 8;
- int64 = {};
- result = exports.parseUInt64.call(int64, parser);
+ result = parseUInt64(parser);
break;
}
if (columnSchema.COLUMN_TYPE.indexOf('unsigned') === -1) {
- var length = size * 8;
+ const length = size * 8;
+ const int64 = (length == 64);
// Flip bits on negative signed integer
if (!int64 && (result & (1 << (length - 1)))) {
result = ((result ^ (Math.pow(2, length) - 1)) * -1) - 1;
- } else if (int64 && (int64.high & (1 << 31))) {
- // Javascript integers only support 2^53 not 2^64, must trim bits!
- // 64-53 = 11, 32-11 = 21, so grab first 21 bits of high word only
- var mask = Math.pow(2, 32) - 1;
- var high = sliceBits(int64.high ^ mask, 0, 21);
- var low = int64.low ^ mask;
- result = ((high * Math.pow(2, 32)) * - 1) - getUInt32Value(low) - 1;
+ } else if (int64 && bigInt(result).greaterOrEquals(bigInt(TWO_TO_POWER_SIXTY_THREE))) {
+ const Max64BitNumber = bigInt('18446744073709551615'); // 2^64 - 1
+ result = bigInt(result).xor(Max64BitNumber).add(1).multiply(-1);
+ // Javascript integers only support 2^53, if not within the range, return a String
+ if (result.greater(Number.MAX_SAFE_INTEGER) || result.lesser(Number.MIN_SAFE_INTEGER)) {
+ result = result.toString();
+ }
+ // Otherwise return a Number
+ else {
+ result = result.toJSNumber();
+ }
}
}
return result;
};
-var readInt24BE = function(buf, offset, noAssert) {
+const readInt24BE = function(buf, offset, noAssert) {
return (buf.readInt8(offset, noAssert) << 16) +
buf.readUInt16BE(offset + 1, noAssert);
};
-var readIntBE = function(buf, offset, length, noAssert) {
+const readIntBE = function(buf, offset, length, noAssert) {
switch (length) {
case 1: return buf.readInt8(offset, noAssert);
case 2: return buf.readInt16BE(offset, noAssert);
@@ -203,26 +185,27 @@ var readIntBE = function(buf, offset, length, noAssert) {
// https://github.com/jeremycole/mysql_binlog/blob/master/lib/mysql_binlog/binlog_field_parser.rb
// Some more information about DECIMAL types:
// http://dev.mysql.com/doc/refman/5.5/en/precision-math-decimal-characteristics.html
-var parseNewDecimal = function(parser, column) {
+const parseNewDecimal = function(parser, column) {
// Constants of format
- var digitsPerInteger = 9;
- var compressedBytes = [0, 1, 1, 2, 2, 3, 3, 4, 4, 4];
+ const digitsPerInteger = 9;
+ const compressedBytes = [0, 1, 1, 2, 2, 3, 3, 4, 4, 4];
- var scale = column.metadata.decimals;
- var integral = column.metadata.precision - scale;
- var uncompIntegral = Math.floor(integral / digitsPerInteger);
- var uncompFractional = Math.floor(scale / digitsPerInteger);
- var compIntegral = integral - (uncompIntegral * digitsPerInteger);
- var compFractional = scale - (uncompFractional * digitsPerInteger);
+ const scale = column.metadata.decimals;
+ const integral = column.metadata.precision - scale;
+ const uncompIntegral = Math.floor(integral / digitsPerInteger);
+ const uncompFractional = Math.floor(scale / digitsPerInteger);
+ const compIntegral = integral - (uncompIntegral * digitsPerInteger);
+ const compFractional = scale - (uncompFractional * digitsPerInteger);
// Grab buffer portion
- var size = (uncompIntegral * 4) + compressedBytes[compIntegral] +
+ const size = (uncompIntegral * 4) + compressedBytes[compIntegral] +
(uncompFractional * 4) + compressedBytes[compFractional];
- var buffer = parser._buffer.slice(parser._offset, parser._offset + size);
+ const buffer = parser._buffer.slice(parser._offset, parser._offset + size);
parser._offset += size; // Move binlog parser position forward
- var str, mask, pos = 0;
- var isPositive = (buffer.readUInt8(0) & (1 << 7)) === 128;
+ let str, mask;
+ let pos = 0;
+ const isPositive = (buffer.readUInt8(0) & (1 << 7)) === 128;
buffer.writeUInt8(buffer.readUInt8(0) ^ (1 << 7), 0);
if (isPositive) {
// Positive number
@@ -235,26 +218,26 @@ var parseNewDecimal = function(parser, column) {
}
// Build integer digits
- var compIntegralSize = compressedBytes[compIntegral];
+ const compIntegralSize = compressedBytes[compIntegral];
if (compIntegralSize > 0) {
str += (readIntBE(buffer, 0, compIntegralSize) ^ mask).toString(10);
pos += compIntegralSize;
}
- for (var i = 0; i < uncompIntegral; i++) {
+ for (let i = 0; i < uncompIntegral; i++) {
str += zeroPad((buffer.readInt32BE(pos) ^ mask).toString(10), 9);
pos += 4;
}
// Build fractional digits
- var fractionDigits = '';
+ let fractionDigits = '';
- for (var k = 0; k < uncompFractional; k++) {
+ for (let k = 0; k < uncompFractional; k++) {
fractionDigits += zeroPad((buffer.readInt32BE(pos) ^ mask).toString(10), 9);
pos += 4;
}
- var compFractionalSize = compressedBytes[compFractional];
+ const compFractionalSize = compressedBytes[compFractional];
if (compFractionalSize > 0) {
fractionDigits += zeroPad((readIntBE(buffer, pos, compFractionalSize) ^ mask).toString(10), compFractional);
}
@@ -267,18 +250,18 @@ var parseNewDecimal = function(parser, column) {
// Did not work in place. Function cribbed from lines 311-363 of
// https://github.com/felixge/node-mysql/blob/cfd0ce3572d75c3c82103418d1d03cbe67eaf8a1/lib/protocol/Parser.js
-var parseGeometryValue = function(buffer) {
- var offset = 4;
+const parseGeometryValue = function(buffer) {
+ let offset = 4;
if (buffer === null || !buffer.length) {
return null;
}
function parseGeometry() {
- var result = null;
- var byteOrder = buffer.readUInt8(offset); offset += 1;
- var wkbType = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
- var x, y, numPoints, i;
+ let result = null;
+ const byteOrder = buffer.readUInt8(offset); offset += 1;
+ const wkbType = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
+ let x, y, numPoints, i;
switch (wkbType) {
case 1: // WKBPoint
@@ -295,13 +278,13 @@ var parseGeometryValue = function(buffer) {
result.push({x: x, y: y});
}
break;
- case 3: // WKBPolygon
- var numRings = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
+ case 3: {// WKBPolygon
+ const numRings = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
result = [];
for (i = numRings; i > 0; i--) {
numPoints = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
- var line = [];
- for (var j=numPoints;j>0;j--) {
+ const line = [];
+ for (let j = numPoints; j > 0; j--) {
x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
line.push({x: x, y: y});
@@ -309,16 +292,18 @@ var parseGeometryValue = function(buffer) {
result.push(line);
}
break;
+ }
case 4: // WKBMultiPoint
case 5: // WKBMultiLineString
case 6: // WKBMultiPolygon
- case 7: // WKBGeometryCollection
- var num = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
+ case 7: {// WKBGeometryCollection
+ const num = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
result = [];
for (i = num; i > 0; i--) {
result.push(parseGeometry());
}
break;
+ }
}
return result;
}
@@ -327,15 +312,15 @@ var parseGeometryValue = function(buffer) {
// Returns false, or an object describing the fraction of a second part of a
// TIME, DATETIME, or TIMESTAMP.
-var readTemporalFraction = function(parser, fractionPrecision) {
+const readTemporalFraction = function(parser, fractionPrecision) {
if (!fractionPrecision) return false;
- var fractionSize = Math.ceil(fractionPrecision / 2);
- var fraction = readIntBE(parser._buffer, parser._offset, fractionSize);
+ let fractionSize = Math.ceil(fractionPrecision / 2);
+ let fraction = readIntBE(parser._buffer, parser._offset, fractionSize);
parser._offset += fractionSize;
if (fractionPrecision % 2 !== 0) fraction /= 10; // Not using full space
if (fraction < 0) fraction *= -1; // Negative time, fraction not negative
- var milliseconds;
+ let milliseconds;
if (fractionPrecision > 3) {
milliseconds = Math.floor(fraction / Math.pow(10, fractionPrecision - 3));
} else if (fractionPrecision < 3) {
@@ -363,15 +348,17 @@ exports.readMysqlValue = function(
zongji // the ZongJi instance, used to read options and emit errors
)
{
- var result;
- var high, low;
- var raw;
- var choices;
- var size, lengthSize;
- var buffer;
- var isNegative;
- var fraction;
- var hour, minute, second;
+ let result;
+ let high, low;
+ let raw;
+ let choices;
+ let size, lengthSize;
+ let buffer;
+ let isNegative;
+ let fraction;
+ let hour, minute, second;
+ let date, time, yearMonth;
+
switch (column.type) {
case MysqlTypes.TINY:
case MysqlTypes.SHORT:
@@ -405,11 +392,11 @@ exports.readMysqlValue = function(
// Second argument: prefixLen = 4 'set('
choices = parseSetEnumTypeDef(columnSchema.COLUMN_TYPE, 4);
result = '';
- for (var i = 0; low >= Math.pow(2, i); i++) {
+ for (let i = 0; low >= Math.pow(2, i); i++) {
if (low & Math.pow(2, i)) result += choices[i] + ',';
}
if (high) {
- for (i = 0; high >= Math.pow(2, i); i++) {
+ for (let i = 0; high >= Math.pow(2, i); i++) {
if (high & Math.pow(2, i)) result += choices[i + 32] + ',';
}
}
@@ -426,14 +413,14 @@ exports.readMysqlValue = function(
result = parser.parseLengthCodedString();
break;
case MysqlTypes.VARCHAR:
- case MysqlTypes.STRING:
- var prefixSize = column.metadata['max_length'] > 255 ? 2 : 1;
+ case MysqlTypes.STRING: {
+ const prefixSize = column.metadata['max_length'] > 255 ? 2 : 1;
size = parser.parseUnsignedNumber(prefixSize);
- var def = columnSchema.COLUMN_TYPE;
- var defPrefix = def.substr(0, 6);
+ const def = columnSchema.COLUMN_TYPE;
+ const defPrefix = def.substr(0, 6);
if (defPrefix === 'binary') {
- result = new Buffer(parseInt(def.substr(7, def.length - 2), 10));
- result.fill(0);
+ const bufsize = parseInt(def.substr(7, def.length - 2), 10);
+ result = Buffer.alloc(bufsize, 0);
parser.parseBuffer(size).copy(result);
} else if (defPrefix === 'varbin') {
result = parser.parseBuffer(size);
@@ -441,6 +428,7 @@ exports.readMysqlValue = function(
result = parser.parseString(size);
}
break;
+ }
case MysqlTypes.TINY_BLOB:
case MysqlTypes.MEDIUM_BLOB:
case MysqlTypes.LONG_BLOB:
@@ -469,7 +457,7 @@ exports.readMysqlValue = function(
result = parseGeometryValue(buffer);
break;
case MysqlTypes.DATE:
- raw = exports.parseUInt24(parser);
+ raw = parseUInt24(parser);
result = dtDecode.getDate(
zongji.connection.config.dateStrings, // node-mysql dateStrings option
sliceBits(raw, 9, 24), // year
@@ -478,7 +466,7 @@ exports.readMysqlValue = function(
);
break;
case MysqlTypes.TIME:
- raw = exports.parseUInt24(parser);
+ raw = parseUInt24(parser);
isNegative = (raw & (1 << 23)) !== 0;
if (isNegative) raw = raw ^ ((1 << 24) - 1); // flip all bits
@@ -519,9 +507,9 @@ exports.readMysqlValue = function(
}
break;
case MysqlTypes.DATETIME:
- raw = exports.parseUInt64(parser);
- var date = Math.floor(raw / 1000000);
- var time = raw % 1000000;
+ raw = parseUInt64(parser);
+ date = Math.floor(raw / 1000000);
+ time = raw % 1000000;
result = dtDecode.getDateTime(
zongji.connection.config.dateStrings, // node-mysql dateStrings option
Math.floor(date / 10000), // year
@@ -532,14 +520,14 @@ exports.readMysqlValue = function(
time % 100 // seconds
);
break;
- case MysqlTypes.DATETIME2:
+ case MysqlTypes.DATETIME2: {
// Overlapping high-low to get all data in 32-bit numbers
- var rawHigh = readIntBE(parser._buffer, parser._offset, 4);
- var rawLow = readIntBE(parser._buffer, parser._offset + 1, 4);
+ const rawHigh = readIntBE(parser._buffer, parser._offset, 4);
+ const rawLow = readIntBE(parser._buffer, parser._offset + 1, 4);
parser._offset += 5;
fraction = readTemporalFraction(parser, column.metadata.decimals);
- var yearMonth = sliceBits(rawHigh, 14, 31);
+ yearMonth = sliceBits(rawHigh, 14, 31);
result = dtDecode.getDateTime(
zongji.connection.config.dateStrings, // node-mysql dateStrings option
Math.floor(yearMonth / 13), // year
@@ -551,6 +539,7 @@ exports.readMysqlValue = function(
fraction // fraction of a second object
);
break;
+ }
case MysqlTypes.TIMESTAMP:
raw = parser.parseUnsignedNumber(4);
result = dtDecode.getTimeStamp(zongji.connection.config.dateStrings, raw);
diff --git a/lib/datetime_decode.js b/lib/datetime_decode.js
index a8793f33..c0e9d304 100644
--- a/lib/datetime_decode.js
+++ b/lib/datetime_decode.js
@@ -3,12 +3,12 @@
// dateStrings option. The dateStrings option should be read from
// zongji.connection.config, where zongji is the current instance of the ZongJi
// object. The dateStrings option is interpreted the same as in node-mysql.
-var common = require('./common'); // used only for common.zeroPad
+const common = require('./common'); // used only for common.zeroPad
// dateStrings are used only if the dateStrings option is true, or is an array
// containing the sql type name string, 'DATE', 'DATETIME', or 'TIMESTAMP'.
// This follows the documentation of the dateStrings option in node-mysql.
-var useDateStringsForType = function(dateStrings, sqlTypeString) {
+const useDateStringsForType = function(dateStrings, sqlTypeString) {
return dateStrings &&
(dateStrings === true ||
dateStrings.indexOf && dateStrings.indexOf(sqlTypeString) > -1);
@@ -16,7 +16,7 @@ var useDateStringsForType = function(dateStrings, sqlTypeString) {
// fraction is the fractional second object from readTemporalFraction().
// returns '' or a '.' followed by fraction.precision digits, like '.123'
-var getFractionString = exports.getFractionString = function(fraction) {
+const getFractionString = exports.getFractionString = function(fraction) {
return fraction ?
'.' + common.zeroPad(fraction.value, fraction.precision) :
'';
@@ -25,7 +25,7 @@ var getFractionString = exports.getFractionString = function(fraction) {
// 1950-00-00 and the like are perfectly valid Mysql dateStrings. A 0 portion
// of a date is essentially a null part of the date, so we should keep it.
// year, month, and date must be integers >= 0. January is month === 1.
-var getDateString = exports.getDateString = function(year, month, date) {
+const getDateString = exports.getDateString = function(year, month, date) {
return common.zeroPad(year, 4) + '-' +
common.zeroPad(month, 2) + '-' +
common.zeroPad(date, 2);
@@ -36,7 +36,7 @@ var getDateString = exports.getDateString = function(year, month, date) {
// which is not what it means. It means 2017-NULL-01, but the Date object
// cannot handle it, so we want to return an invalid month, rather than a
// subtracted month.
-var jsMonthFromMysqlMonth = function(month) {
+const jsMonthFromMysqlMonth = function(month) {
return month > 0 ? month - 1 : undefined;
};
@@ -98,8 +98,8 @@ exports.getTimeStamp = function(dateStrings, // node-mysql dateStrings option
fraction // optional fraction of second object
)
{
- var milliseconds = fraction ? fraction.milliseconds : 0;
- var dateObject = new Date(secondsFromEpoch * 1000 + milliseconds);
+ const milliseconds = fraction ? fraction.milliseconds : 0;
+ const dateObject = new Date(secondsFromEpoch * 1000 + milliseconds);
if (!useDateStringsForType(dateStrings, 'TIMESTAMP')) {
return dateObject;
}
diff --git a/lib/json_decode.js b/lib/json_decode.js
index 845b20c3..d7a0a5cc 100644
--- a/lib/json_decode.js
+++ b/lib/json_decode.js
@@ -1,25 +1,25 @@
-var common = require('./common');
-var Parser = require('mysql/lib/protocol/Parser');
+const common = require('./common');
+const Parser = require('mysql/lib/protocol/Parser');
-var JSONB_TYPE_SMALL_OBJECT = 0;
-var JSONB_TYPE_LARGE_OBJECT = 1;
-var JSONB_TYPE_SMALL_ARRAY = 2;
-var JSONB_TYPE_LARGE_ARRAY = 3;
-var JSONB_TYPE_LITERAL = 4;
-var JSONB_TYPE_INT16 = 5;
-var JSONB_TYPE_UINT16 = 6;
-var JSONB_TYPE_INT32 = 7;
-var JSONB_TYPE_UINT32 = 8;
-var JSONB_TYPE_INT64 = 9;
-var JSONB_TYPE_UINT64 = 10;
-var JSONB_TYPE_DOUBLE = 11;
-var JSONB_TYPE_STRING = 12;
-var JSONB_TYPE_OPAQUE = 15;
+const JSONB_TYPE_SMALL_OBJECT = 0;
+const JSONB_TYPE_LARGE_OBJECT = 1;
+const JSONB_TYPE_SMALL_ARRAY = 2;
+const JSONB_TYPE_LARGE_ARRAY = 3;
+const JSONB_TYPE_LITERAL = 4;
+const JSONB_TYPE_INT16 = 5;
+const JSONB_TYPE_UINT16 = 6;
+const JSONB_TYPE_INT32 = 7;
+const JSONB_TYPE_UINT32 = 8;
+const JSONB_TYPE_INT64 = 9;
+const JSONB_TYPE_UINT64 = 10;
+const JSONB_TYPE_DOUBLE = 11;
+const JSONB_TYPE_STRING = 12;
+const JSONB_TYPE_OPAQUE = 15;
-var JSONB_LITERALS = [ null, true, false ];
+const JSONB_LITERALS = [ null, true, false ];
// node-mysql prefixes binary string values
-var VAR_STRING_PREFIX = 'base64:type253:';
+const VAR_STRING_PREFIX = 'base64:type253:';
module.exports = function(input) {
// Value must be JSON string to match node-mysql results
@@ -48,13 +48,11 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
// Only used for types which use the value stored at a pointer position
// If object is root (offset 0), the value is not offset by pointer
- var valueOffset = offset === 0 ? 0 :
+ const valueOffset = offset === 0 ? 0 :
readUInt(offset + 1) + parentValueOffset;
- var result = null;
- var jsonType = input.readUInt8(offset);
- var low, high;
- var raw, fraction, yearMonth;
+ let result = null;
+ const jsonType = input.readUInt8(offset);
switch (jsonType) {
// Small enough types are inlined
case JSONB_TYPE_INT16:
@@ -64,13 +62,14 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
// XXX: No known instance of this type being used
result = input.readUInt16LE(offset + 1);
break;
- case JSONB_TYPE_LITERAL:
- var inlineValue = input.readUInt8(offset + 1);
+ case JSONB_TYPE_LITERAL: {
+ const inlineValue = input.readUInt8(offset + 1);
result = JSONB_LITERALS[inlineValue];
break;
+ }
// All other types are retrieved from pointer
- case JSONB_TYPE_STRING:
- var strLen, strLenSize = 0, curStrLenByte;
+ case JSONB_TYPE_STRING: {
+ let strLen, strLenSize = 0, curStrLenByte;
// If the high bit is 1, the string length continues to the next byte
while (strLenSize === 0 || (curStrLenByte & 128) === 128) {
strLenSize++;
@@ -86,6 +85,7 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
valueOffset + strLenSize + 1,
valueOffset + strLenSize + 1 + strLen);
break;
+ }
case JSONB_TYPE_LARGE_OBJECT:
result = readObject(input, valueOffset, true);
break;
@@ -98,11 +98,12 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
case JSONB_TYPE_SMALL_ARRAY:
result = readArray(input, valueOffset, false);
break;
- case JSONB_TYPE_DOUBLE:
- low = input.readUInt32LE(valueOffset + 1);
- high = input.readUInt32LE(valueOffset + 5);
+ case JSONB_TYPE_DOUBLE: {
+ const low = input.readUInt32LE(valueOffset + 1);
+ const high = input.readUInt32LE(valueOffset + 5);
result = common.parseIEEE754Float(high, low);
break;
+ }
case JSONB_TYPE_INT32:
result = input.readInt32LE(valueOffset + 1);
break;
@@ -110,13 +111,13 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
// XXX: No known instance of this type being used
result = input.readUInt32LE(valueOffset + 1);
break;
- case JSONB_TYPE_INT64:
- low = input.readUInt32LE(valueOffset + 1);
- high = input.readUInt32LE(valueOffset + 5);
+ case JSONB_TYPE_INT64: {
+ let low = input.readUInt32LE(valueOffset + 1);
+ let high = input.readUInt32LE(valueOffset + 5);
if (high & (1 << 31)) {
// Javascript integers only support 2^53 not 2^64, must trim bits!
// 64-53 = 11, 32-11 = 21, so grab first 21 bits of high word only
- var mask = Math.pow(2, 32) - 1;
+ const mask = Math.pow(2, 32) - 1;
high = common.sliceBits(high ^ mask, 0, 21);
low = low ^ mask;
result =
@@ -125,14 +126,16 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
result = (high * Math.pow(2,32)) + low;
}
break;
- case JSONB_TYPE_UINT64:
- low = input.readUInt32LE(valueOffset + 1);
- high = input.readUInt32LE(valueOffset + 5);
+ }
+ case JSONB_TYPE_UINT64: {
+ const low = input.readUInt32LE(valueOffset + 1);
+ const high = input.readUInt32LE(valueOffset + 5);
result = (high * Math.pow(2,32)) + low;
break;
- case JSONB_TYPE_OPAQUE:
- var customType = input.readUInt8(valueOffset + 1);
- var dataLen, dataLenSize = 0, curDataLenByte;
+ }
+ case JSONB_TYPE_OPAQUE: {
+ const customType = input.readUInt8(valueOffset + 1);
+ let dataLen, dataLenSize = 0, curDataLenByte;
// If the high bit is 1, the string length continues to the next byte
while (dataLenSize === 0 || (curDataLenByte & 128) === 128) {
dataLenSize++;
@@ -147,28 +150,29 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
// Configure parser and metadata if using standard readMysqlValue
// from common.js, otherwise set result for custom decoding
- var parser = new Parser();
- var metadata = {};
- var parseType = customType;
+ const parser = new Parser();
+ let metadata = {};
+ const parseType = customType;
parser.append(input.slice(
valueOffset + dataLenSize + 2,
valueOffset + dataLenSize + 2 + dataLen));
switch (customType) {
- case common.MysqlTypes.DATE:
- raw = parser._buffer.readInt32LE(4);
- yearMonth = common.sliceBits(raw, 14, 31);
+ case common.MysqlTypes.DATE: {
+ const raw = parser._buffer.readInt32LE(4);
+ const yearMonth = common.sliceBits(raw, 14, 31);
result =
common.zeroPad(Math.floor(yearMonth / 13), 4) + '-' + // year
common.zeroPad(yearMonth % 13, 2) + '-' + // month
common.zeroPad(common.sliceBits(raw, 9, 14), 2); // day
break;
- case common.MysqlTypes.TIME:
- raw = parser._buffer.readUInt32LE(3);
- fraction = common.sliceBits(parser._buffer.readInt32LE(0), 0, 24);
+ }
+ case common.MysqlTypes.TIME: {
+ let raw = parser._buffer.readUInt32LE(3);
+ let fraction = common.sliceBits(parser._buffer.readInt32LE(0), 0, 24);
- var isNegative = (raw & (1 << 23)) !== 0;
+ const isNegative = (raw & (1 << 23)) !== 0;
if (isNegative) {
raw = (raw ^ ((1 << 24) - 1)) + 1; // flip all bits
// If fraction exists, last bit adjustment goes to microseconds
@@ -178,9 +182,9 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
}
}
- var hour = common.sliceBits(raw, 12, 22);
- var minute = common.sliceBits(raw, 6, 12);
- var second = common.sliceBits(raw, 0, 6);
+ const hour = common.sliceBits(raw, 12, 22);
+ const minute = common.sliceBits(raw, 6, 12);
+ const second = common.sliceBits(raw, 0, 6);
result = (isNegative ? '-' : '') +
common.zeroPad(hour, hour > 99 ? 3 : 2) + ':' +
@@ -188,13 +192,14 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
common.zeroPad(second, 2) +
'.' + common.zeroPad(fraction, 6);
break;
- case common.MysqlTypes.DATETIME:
+ }
+ case common.MysqlTypes.DATETIME: {
// Overlapping high-low to get all data in 32-bit numbers
- var rawHigh = parser._buffer.readUInt32LE(3);
- var rawLow = parser._buffer.readUInt32LE(4);
- fraction = common.sliceBits(parser._buffer.readInt32LE(0), 0, 24);
+ const rawHigh = parser._buffer.readUInt32LE(3);
+ const rawLow = parser._buffer.readUInt32LE(4);
+ const fraction = common.sliceBits(parser._buffer.readInt32LE(0), 0, 24);
- yearMonth = common.sliceBits(rawLow, 14, 31);
+ const yearMonth = common.sliceBits(rawLow, 14, 31);
result =
common.zeroPad(Math.floor(yearMonth / 13), 4) + '-' + // year
common.zeroPad(yearMonth % 13, 2) + '-' + // month
@@ -204,6 +209,7 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
common.zeroPad(common.sliceBits(rawHigh, 0, 6), 2) + '.' + // seconds
common.zeroPad(fraction, 6);
break;
+ }
case common.MysqlTypes.NEWDECIMAL:
metadata = {
precision: parser.parseUnsignedNumber(1),
@@ -226,6 +232,7 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
});
}
break;
+ }
default:
throw new Error('JSON Type Not Implemented: ' + jsonType);
}
@@ -233,7 +240,7 @@ function parseBinaryBuffer(input, offset, parentValueOffset, readUInt) {
}
function readObject(input, valueOffset, isLarge) {
- var readUInt, intSize;
+ let readUInt, intSize;
if (isLarge) {
readUInt = input.readUInt32LE.bind(input);
intSize = 4;
@@ -242,26 +249,26 @@ function readObject(input, valueOffset, isLarge) {
intSize = 2;
}
- var result = {};
- var memberCount = readUInt(valueOffset + 1); // +1 = JSON type byte
+ const result = {};
+ const memberCount = readUInt(valueOffset + 1); // +1 = JSON type byte
// Position where key entries start
// Key entry: Key offset (int16/32) + Key length (int16)
- var memberKeyStart =
+ const memberKeyStart =
valueOffset + 1 + // Beginning of definition
(intSize * 2); // memberCount + binarySize
// Value entries (or pointers to such) begin after key entries
- var memberValueStart = memberKeyStart + (memberCount * (intSize + 2));
+ const memberValueStart = memberKeyStart + (memberCount * (intSize + 2));
- for (var pointerPos = 0; pointerPos < memberCount; pointerPos++) {
- var keyEntryPos = memberKeyStart + (pointerPos * (intSize + 2));
+ for (let pointerPos = 0; pointerPos < memberCount; pointerPos++) {
+ const keyEntryPos = memberKeyStart + (pointerPos * (intSize + 2));
- var keyStart = valueOffset + 1 + readUInt(keyEntryPos);
- var keyEnd = keyStart + input.readUInt16LE(keyEntryPos + intSize);
+ const keyStart = valueOffset + 1 + readUInt(keyEntryPos);
+ const keyEnd = keyStart + input.readUInt16LE(keyEntryPos + intSize);
- var thisKey = input.toString('utf8', keyStart, keyEnd);
- var memberValueOffset = memberValueStart + (pointerPos * (intSize + 1));
+ const thisKey = input.toString('utf8', keyStart, keyEnd);
+ const memberValueOffset = memberValueStart + (pointerPos * (intSize + 1));
result[thisKey] =
parseBinaryBuffer(input, memberValueOffset, valueOffset, readUInt);
@@ -271,7 +278,7 @@ function readObject(input, valueOffset, isLarge) {
}
function readArray(input, valueOffset, isLarge) {
- var readUInt, intSize;
+ let readUInt, intSize;
if (isLarge) {
readUInt = input.readUInt32LE.bind(input);
intSize = 4;
@@ -280,11 +287,11 @@ function readArray(input, valueOffset, isLarge) {
intSize = 2;
}
- var result = [];
- var memberCount = readUInt(valueOffset + 1); // +1 = JSON type byte
+ const result = [];
+ const memberCount = readUInt(valueOffset + 1); // +1 = JSON type byte
- for (var pointerPos = 0; pointerPos < memberCount; pointerPos++) {
- var memberValueOffset =
+ for (let pointerPos = 0; pointerPos < memberCount; pointerPos++) {
+ let memberValueOffset =
valueOffset + 1 + // Beginning of definition
(intSize * 2) + // memberCount + binarySize
(pointerPos * (1 + intSize)); // value type + value offset
diff --git a/lib/packet/binlog.js b/lib/packet/binlog.js
new file mode 100644
index 00000000..1db4d531
--- /dev/null
+++ b/lib/packet/binlog.js
@@ -0,0 +1,67 @@
+const getEventClass = require('../code_map').getEventClass;
+
+//TODO Don't depend on zongji instance here
+module.exports = function initBinlogPacketClass(zongji) {
+
+ class BinlogPacket {
+
+ *_process(parser) {
+ // uint8_t marker; // always 0 or 0xFF
+ // uint32_t timestamp;
+ // uint8_t type_code;
+ // uint32_t server_id;
+ // uint32_t event_length;
+ // uint32_t next_position;
+ // uint16_t flags;
+ parser.parseUnsignedNumber(1);
+
+ const timestamp = parser.parseUnsignedNumber(4) * 1000;
+ const eventType = parser.parseUnsignedNumber(1);
+ parser.parseUnsignedNumber(4); // serverId
+ const eventLength = parser.parseUnsignedNumber(4);
+ const nextPosition = parser.parseUnsignedNumber(4);
+ parser.parseUnsignedNumber(2); // flags
+
+ const options = {
+ timestamp: timestamp,
+ nextPosition: nextPosition,
+ size: eventLength - BinlogPacket.Length,
+ eventType: eventType,
+ };
+
+ const EventClass = getEventClass(eventType);
+ this.eventName = EventClass.name;
+
+ yield;
+
+ try {
+ this._event = new EventClass(parser, options, zongji);
+ } catch (err) {
+ // Record error occurence but suppress until handled
+ this._error = err;
+ }
+ }
+
+ // interface will be called, see mysql/lib/protocol/Protocol
+ parse(parser) {
+ this._processor = this._process(parser);
+ this._processor.next();
+ }
+
+ getEvent() {
+ this._processor.next();
+ // Ready to handle the error now
+ if (this._error) throw this._error;
+ return this._event;
+ }
+ }
+
+ // header length doesn't count marker
+ BinlogPacket.Length = 19;
+
+ if (zongji.useChecksum) {
+ BinlogPacket.Length = 19 + 4;
+ }
+
+ return BinlogPacket;
+};
diff --git a/lib/packet/binlog_header.js b/lib/packet/binlog_header.js
deleted file mode 100644
index 40cc9478..00000000
--- a/lib/packet/binlog_header.js
+++ /dev/null
@@ -1,63 +0,0 @@
-var getEventClass = require('../code_map').getEventClass;
-
-module.exports = function generateBinlogHeader(options) {
- var zongji = this;
- var tableMap = options.tableMap;
- var useChecksum = options.useChecksum;
-
- function BinlogHeader() {}
-
- BinlogHeader.prototype.parse = function(parser) {
- // uint8_t marker; // always 0 or 0xFF
- // uint32_t timestamp;
- // uint8_t type_code;
- // uint32_t server_id;
- // uint32_t event_length;
- // uint32_t next_position;
- // uint16_t flags;
- parser.parseUnsignedNumber(1);
-
- var timestamp = parser.parseUnsignedNumber(4) * 1000;
- var eventType = parser.parseUnsignedNumber(1);
- var serverId = parser.parseUnsignedNumber(4); // eslint-disable-line
- var eventLength = parser.parseUnsignedNumber(4);
- var nextPosition = parser.parseUnsignedNumber(4);
- var flags = parser.parseUnsignedNumber(2); // eslint-disable-line
-
- // headerLength doesn't count marker
- var headerLength = 19;
- // for MySQL 5.6 and binlog-checksum = CRC32
- if (useChecksum) {
- headerLength += 4;
- }
- var eventSize = eventLength - headerLength;
-
- var options = {
- timestamp: timestamp,
- nextPosition: nextPosition,
- size: eventSize,
- eventType: eventType,
- tableMap: tableMap,
- };
-
- var EventClass = getEventClass(eventType);
- // Check event filtering
- if (!zongji._skipEvent(EventClass.name.toLowerCase())) {
- try {
- this._event = new EventClass(parser, options, zongji);
- } catch (err) {
- // Record error occurence but suppress until handled
- this._error = err;
- }
- }
- };
-
- BinlogHeader.prototype.getEvent = function() {
- // Ready to handle the error now
- if (this._error) throw this._error;
- return this._event;
- };
-
- return BinlogHeader;
-};
-
diff --git a/lib/packet/combinlog.js b/lib/packet/combinlog.js
index 3ab42e03..f6d7361a 100644
--- a/lib/packet/combinlog.js
+++ b/lib/packet/combinlog.js
@@ -1,14 +1,13 @@
-function ComBinlog(options) {
- options = options || {};
+function ComBinlog({ serverId, nonBlock, filename, position }) {
this.command = 0x12;
- this.position = options.position || 4;
+ this.position = position || 4;
// will send eof package if there is no more binlog event
// https://dev.mysql.com/doc/internals/en/com-binlog-dump.html#binlog-dump-non-block
- this.flags = options.nonBlock ? 1 : 0;
+ this.flags = nonBlock ? 1 : 0;
- this.serverId = options.serverId || 1;
- this.filename = options.filename || '';
+ this.serverId = serverId || 1;
+ this.filename = filename || '';
}
ComBinlog.prototype.write = function(writer) {
diff --git a/lib/packet/index.js b/lib/packet/index.js
index 44db2d32..3f9f7da4 100644
--- a/lib/packet/index.js
+++ b/lib/packet/index.js
@@ -60,7 +60,7 @@ ErrorPacket.prototype.write = function(writer) {
writer.writeString(this.message);
};
-exports.Eof = EofPacket;
-exports.Error = ErrorPacket;
+exports.EofPacket = EofPacket;
+exports.ErrorPacket = ErrorPacket;
exports.ComBinlog = require('./combinlog');
-exports.initBinlogHeader = require('./binlog_header');
+exports.initBinlogPacketClass = require('./binlog');
diff --git a/lib/reader.js b/lib/reader.js
index 97ad43ea..e9a24039 100644
--- a/lib/reader.js
+++ b/lib/reader.js
@@ -1,9 +1,9 @@
// Constants for variable length encoded binary
-var NULL_COLUMN = 251;
-var UNSIGNED_CHAR_COLUMN = 251;
-var UNSIGNED_SHORT_COLUMN = 252;
-var UNSIGNED_INT24_COLUMN = 253;
-var UNSIGNED_INT64_COLUMN = 254;
+const NULL_COLUMN = 251;
+const UNSIGNED_CHAR_COLUMN = 251;
+const UNSIGNED_SHORT_COLUMN = 252;
+const UNSIGNED_INT24_COLUMN = 253;
+const UNSIGNED_INT64_COLUMN = 254;
function BufferReader(buffer) {
this.buffer = buffer;
@@ -11,34 +11,34 @@ function BufferReader(buffer) {
}
BufferReader.prototype.readUInt8 = function() {
- var pos = this.position;
+ const pos = this.position;
this.position += 1;
return this.buffer.readUInt8(pos);
};
BufferReader.prototype.readUInt16 = function() {
- var pos = this.position;
+ const pos = this.position;
this.position += 2;
return this.buffer.readUInt16LE(pos);
};
BufferReader.prototype.readUInt32 = function() {
- var pos = this.position;
+ const pos = this.position;
this.position += 4;
return this.buffer.readUInt32LE(pos);
};
BufferReader.prototype.readUInt24 = function() {
- var low = this.readUInt16();
- var high = this.readUInt8();
+ const low = this.readUInt16();
+ const high = this.readUInt8();
return (high << 16) + low;
};
BufferReader.prototype.readUInt64 = function() {
- var pos = this.position;
+ const pos = this.position;
this.position += 8;
// from http://stackoverflow.com/questions/17687307/convert-a-64bit-little-endian-integer-to-number
@@ -47,30 +47,30 @@ BufferReader.prototype.readUInt64 = function() {
};
BufferReader.prototype.readString = function() {
- var strBuf = this.buffer.slice(this.position);
+ const strBuf = this.buffer.slice(this.position);
this.position = this.buffer.length;
return strBuf.toString('ascii');
};
BufferReader.prototype.readStringInBytes = function(length) {
- var strBuf = this.buffer.slice(this.position, this.position + length);
+ const strBuf = this.buffer.slice(this.position, this.position + length);
this.position += length;
return strBuf.toString('ascii');
};
BufferReader.prototype.readHexInBytes = function(length) {
- var buf = this.buffer.slice(this.position, this.position + length);
+ const buf = this.buffer.slice(this.position, this.position + length);
this.position += length;
return buf.toString('hex');
};
BufferReader.prototype.readBytesArray = function(length) {
- var result = [];
- var hexString = this.readHexInBytes(length);
- for (var i = 0; i < hexString.length; i = i + 2) {
+ const result = [];
+ const hexString = this.readHexInBytes(length);
+ for (let i = 0; i < hexString.length; i = i + 2) {
result.push(parseInt(hexString.substr(i, 2), 16));
}
return result;
@@ -83,8 +83,8 @@ BufferReader.prototype.readBytesArray = function(length) {
// used to store the actual value, which can be 2, 3, or 8. It also
// includes support for SQL NULL as a special case.
BufferReader.prototype.readVariant = function() {
- var result = null;
- var firstByte = this.readUInt8();
+ let result = null;
+ const firstByte = this.readUInt8();
if (firstByte < UNSIGNED_CHAR_COLUMN) {
result = firstByte;
@@ -103,11 +103,11 @@ BufferReader.prototype.readVariant = function() {
return result;
};
-var padWith = function(val, length) {
- var bits = val.split('');
+const padWith = function(val, length) {
+ const bits = val.split('');
if (bits.length < length) {
- var left = length - bits.length;
- for (var j = left - 1; j >= 0; j--) {
+ const left = length - bits.length;
+ for (let j = left - 1; j >= 0; j--) {
bits.unshift('0');
}
val = bits.join('');
@@ -119,19 +119,19 @@ var padWith = function(val, length) {
// Read an arbitrary-length bitmap, provided its length.
// Returns an array of true/false values.
BufferReader.prototype.readBitArray = function(length) {
- var size = Math.floor((length + 7) / 8);
+ const size = Math.floor((length + 7) / 8);
- var bytes = [];
- for (var i = size - 1; i >= 0; i--) {
+ const bytes = [];
+ for (let i = size - 1; i >= 0; i--) {
bytes.unshift(this.readUInt8());
}
- var bitmap = [];
- var bitmapStr = bytes.map(function(aByte) {
+ const bitmap = [];
+ const bitmapStr = bytes.map(function(aByte) {
return padWith(aByte.toString(2), 8);
}).join('');
- for (var k = bitmapStr.length - 1; k >= 0; k--) {
+ for (let k = bitmapStr.length - 1; k >= 0; k--) {
bitmap.push(bitmapStr[k] === '1');
}
diff --git a/lib/rows_event.js b/lib/rows_event.js
index 68960fea..3ad35c30 100644
--- a/lib/rows_event.js
+++ b/lib/rows_event.js
@@ -1,14 +1,35 @@
-var util = require('util');
-var BinlogEvent = require('./binlog_event').BinlogEvent;
-var Common = require('./common');
+const util = require('util');
+const BinlogEvent = require('./binlog_event').BinlogEvent;
+const Common = require('./common');
-var Version2Events = [
+const Version2Events = [
0x1e, // WRITE_ROWS_EVENT_V2,
0x1f, // UPDATE_ROWS_EVENT_V2,
0x20, // DELETE_ROWS_EVENT_V2
];
-var CHECKSUM_SIZE = 4;
+const CHECKSUM_SIZE = 4;
+
+// A quick way to know how many bits set in a given byte
+// e.g. Given 3 => 0000 0011, it has 2 bits set
+const BIT_COUNT_MAP_IN_ONE_BYTE = [
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
+];
/**
* Generic RowsEvent class
@@ -35,21 +56,19 @@ function RowsEvent(parser, options, zongji) {
// Body
this.numberOfColumns = parser.parseLengthCodedNumber();
- this.tableMap = options.tableMap;
+ this.tableMap = zongji.tableMap;
- var tableData = this.tableMap[this.tableId];
+ const tableData = this.tableMap[this.tableId];
if (tableData === undefined) {
// TableMap event was filtered
parser._offset = parser._packetEnd;
this._filtered = true;
} else {
- var columnsPresentBitmapSize = Math.floor((this.numberOfColumns + 7) / 8);
- // Columns present bitmap exceeds 4 bytes with >32 rows
- // And is not handled anyways so just skip over its space
- parser._offset += columnsPresentBitmapSize;
+ const columnsPresentBitmapSize = Math.floor((this.numberOfColumns + 7) / 8);
+ this.columns_present_bitmap = parser.parseBuffer(columnsPresentBitmapSize);
if (this._hasTwoRows) {
// UpdateRows event slightly different, has new and old rows represented
- parser._offset += columnsPresentBitmapSize;
+ this.columns_present_bitmap2 = parser.parseBuffer(columnsPresentBitmapSize);
}
if (this.useChecksum) {
@@ -90,28 +109,46 @@ RowsEvent.prototype.dump = function() {
};
RowsEvent.prototype._fetchOneRow = function(parser) {
- return readRow(this.tableMap[this.tableId], parser, this._zongji);
+ const tablemap = this.tableMap[this.tableId];
+ return readRow(parser, tablemap, this.columns_present_bitmap, this._zongji);
};
-var readRow = function(tableMap, parser, zongji) {
- var row = {}, column, columnSchema;
- var nullBitmapSize = Math.floor((tableMap.columns.length + 7) / 8);
- var nullBuffer = parser._buffer.slice(parser._offset,
- parser._offset + nullBitmapSize);
- var curNullByte, curBit;
- parser._offset += nullBitmapSize;
-
- for (var i = 0; i < tableMap.columns.length; i++) {
- curBit = i % 8;
- if (curBit === 0) curNullByte = nullBuffer.readUInt8(Math.floor(i / 8));
- column = tableMap.columns[i];
- columnSchema = tableMap.columnSchemas[i];
- if ((curNullByte & (1 << curBit)) === 0) {
- row[column.name] =
- Common.readMysqlValue(parser, column, columnSchema, tableMap, zongji);
- } else {
+const countBits = function(buff) {
+ let bits = 0;
+ for (let i = 0; i < buff.length; i++) {
+ bits += BIT_COUNT_MAP_IN_ONE_BYTE[buff[i]];
+ }
+ return bits;
+};
+
+const getBit = function(buff, position) {
+ let byte = buff[Math.floor(position / 8)];
+ return byte & (1 << (position % 8));
+};
+
+const readRow = function(parser, tablemap, bitmap, zongji) {
+ const nullBitmapSize = Math.floor((countBits(bitmap) + 7) / 8);
+ const nullBitmap = parser.parseBuffer(nullBitmapSize);
+
+ let row = {};
+ for (let i = 0, nullBitIndex = 0; i < tablemap.columns.length; i++) {
+ let column = tablemap.columns[i];
+
+ if (getBit(bitmap, i) == 0) {
row[column.name] = null;
+ continue;
}
+
+ if (getBit(nullBitmap, nullBitIndex) != 0) {
+ row[column.name] = null;
+ } else {
+ let columnSchema = tablemap.columnSchemas[i];
+ row[column.name] = Common.readMysqlValue(
+ parser, column, columnSchema, tablemap, zongji
+ );
+ }
+
+ nullBitIndex += 1;
}
return row;
};
@@ -138,10 +175,10 @@ function UpdateRows(parser, options) { // eslint-disable-line
util.inherits(UpdateRows, RowsEvent);
UpdateRows.prototype._fetchOneRow = function(parser) {
- var tableMap = this.tableMap[this.tableId];
+ const tablemap = this.tableMap[this.tableId];
return {
- before: readRow(tableMap, parser, this._zongji),
- after: readRow(tableMap, parser, this._zongji)
+ before: readRow(parser, tablemap, this.columns_present_bitmap, this._zongji),
+ after: readRow(parser, tablemap, this.columns_present_bitmap2, this._zongji),
};
};
diff --git a/lib/sequence/binlog.js b/lib/sequence/binlog.js
index dde26625..74bff202 100644
--- a/lib/sequence/binlog.js
+++ b/lib/sequence/binlog.js
@@ -1,11 +1,9 @@
-var Util = require('util');
-var Packet = require('../packet');
-var capture = require('../capture');
+const Util = require('util');
+const { EofPacket, ErrorPacket, ComBinlog, initBinlogPacketClass } = require('../packet');
+const Sequence = require('mysql/lib/protocol/sequences').Sequence;
-module.exports = function(options) {
- var self = this; // ZongJi instance
- var Sequence = capture(self.connection).Sequence;
- var BinlogHeader = Packet.initBinlogHeader.call(self, options);
+module.exports = function(zongji) {
+ const BinlogPacket = initBinlogPacketClass(zongji);
function Binlog(callback) {
Sequence.call(this, callback);
@@ -14,17 +12,21 @@ module.exports = function(options) {
Util.inherits(Binlog, Sequence);
Binlog.prototype.start = function() {
- this.emit('packet', new Packet.ComBinlog(options));
+ // options include: position / nonBlock / serverId / filename
+ let options = zongji.get([
+ 'serverId', 'position', 'filename', 'nonBlock',
+ ]);
+ this.emit('packet', new ComBinlog(options));
};
Binlog.prototype.determinePacket = function(firstByte) {
switch (firstByte) {
case 0xfe:
- return Packet.Eof;
+ return EofPacket;
case 0xff:
- return Packet.Error;
+ return ErrorPacket;
default:
- return BinlogHeader;
+ return BinlogPacket;
}
};
@@ -32,9 +34,15 @@ module.exports = function(options) {
console.log('Received one OkPacket ...');
};
- Binlog.prototype['BinlogHeader'] = function(packet) {
+ Binlog.prototype['BinlogPacket'] = function(packet) {
if (this._callback) {
- var event, error;
+
+ // Check event filtering
+ if (zongji._skipEvent(packet.eventName.toLowerCase())) {
+ return this._callback.call(this);
+ }
+
+ let event, error;
try {
event = packet.getEvent();
} catch (err) {
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..110321e0
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,4079 @@
+{
+ "name": "zongji",
+ "version": "0.5.1",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+ "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.3"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.9.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.3.tgz",
+ "integrity": "sha512-RpxM252EYsz9qLUIq6F7YJyK1sv0wWDBFuztfDGWaQKzHjqDHysxSiRUpA/X9jmfqo+WzkAVKFaUily5h+gDCQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.9.0",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.13",
+ "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/helper-function-name": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz",
+ "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.8.3",
+ "@babel/template": "^7.8.3",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
+ "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
+ "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz",
+ "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+ "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.0",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.9.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.3.tgz",
+ "integrity": "sha512-E6SpIDJZ0cZAKoCNk+qSDd0ChfTnpiJN9FfNf3RZ20dzwA2vL2oq5IX1XTVT+4vDmRlta2nGk5HGMMskJAR+4A==",
+ "dev": true
+ },
+ "@babel/runtime": {
+ "version": "7.9.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
+ "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "@babel/template": {
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz",
+ "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.9.0",
+ "@babel/helper-function-name": "^7.8.3",
+ "@babel/helper-split-export-declaration": "^7.8.3",
+ "@babel/parser": "^7.9.0",
+ "@babel/types": "^7.9.0",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.13"
+ },
+ "dependencies": {
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/types": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz",
+ "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.0",
+ "lodash": "^4.17.13",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@types/color-name": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+ "dev": true
+ },
+ "acorn": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
+ "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz",
+ "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.12.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
+ "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-escapes": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
+ "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.11.0"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
+ "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==",
+ "dev": true
+ }
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "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"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "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"
+ }
+ },
+ "archy": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
+ "dev": true
+ },
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "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-hook-domain": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-1.1.3.tgz",
+ "integrity": "sha512-ZovMxSbADV3+biB7oR1GL5lGyptI24alp0LWHlmz1OFc5oL47pz3EiIF6nXOkDW7yLqih4NtsiYduzdDW0i+Wg==",
+ "dev": true,
+ "requires": {
+ "source-map-support": "^0.5.11"
+ }
+ },
+ "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.9.1",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
+ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.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"
+ }
+ },
+ "big-integer": {
+ "version": "1.6.48",
+ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
+ "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w=="
+ },
+ "bignumber.js": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
+ "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
+ },
+ "binary-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
+ "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
+ "dev": true
+ },
+ "bind-obj-methods": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-2.0.0.tgz",
+ "integrity": "sha512-3/qRXczDi2Cdbz6jE+W3IflJOutRVica8frpBn14de1mBOkzDo+6tY33kNhvkw54Kn3PzRRD2VnGbGPcTAk4sw==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browser-process-hrtime": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+ "dev": true
+ },
+ "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
+ },
+ "caching-transform": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz",
+ "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==",
+ "dev": true,
+ "requires": {
+ "hasha": "^3.0.0",
+ "make-dir": "^2.0.0",
+ "package-hash": "^3.0.0",
+ "write-file-atomic": "^2.4.2"
+ },
+ "dependencies": {
+ "write-file-atomic": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
+ "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.2"
+ }
+ }
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "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
+ },
+ "chokidar": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
+ "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.3.0"
+ }
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.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
+ },
+ "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"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "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
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+ "dev": true
+ },
+ "color-convert": {
+ "version": "1.9.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
+ },
+ "color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+ "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "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="
+ },
+ "coveralls": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.11.tgz",
+ "integrity": "sha512-LZPWPR2NyGKyaABnc49dR0fpeP6UqhvGq4B5nUrTQ1UBy55z96+ga7r+/ChMdMJUwBgyJDXBi88UBgz2rs9IiQ==",
+ "dev": true,
+ "requires": {
+ "js-yaml": "^3.13.1",
+ "lcov-parse": "^1.0.0",
+ "log-driver": "^1.2.7",
+ "minimist": "^1.2.5",
+ "request": "^2.88.0"
+ }
+ },
+ "cp-file": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz",
+ "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "make-dir": "^2.0.0",
+ "nested-error-stacks": "^2.0.0",
+ "pify": "^4.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "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"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "diff-frag": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/diff-frag/-/diff-frag-1.0.1.tgz",
+ "integrity": "sha512-6/v2PC/6UTGcWPPetb9acL8foberUg/CtPdALeJUdD1B/weHNvzftoo00gYznqHGRhHEbykUGzqfG9RWOSr5yw==",
+ "dev": true
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "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"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "es6-error": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
+ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+ "dev": true
+ },
+ "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": "6.8.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
+ "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "ajv": "^6.10.0",
+ "chalk": "^2.1.0",
+ "cross-spawn": "^6.0.5",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^1.4.3",
+ "eslint-visitor-keys": "^1.1.0",
+ "espree": "^6.1.2",
+ "esquery": "^1.0.1",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^5.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^7.0.0",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.14",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.3",
+ "progress": "^2.0.0",
+ "regexpp": "^2.0.1",
+ "semver": "^6.1.2",
+ "strip-ansi": "^5.2.0",
+ "strip-json-comments": "^3.0.1",
+ "table": "^5.2.3",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ }
+ },
+ "eslint-scope": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
+ "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+ "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+ "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+ "dev": true
+ },
+ "esm": {
+ "version": "3.2.25",
+ "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
+ "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
+ "dev": true
+ },
+ "espree": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
+ "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.1.1",
+ "acorn-jsx": "^5.2.0",
+ "eslint-visitor-keys": "^1.1.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.1.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz",
+ "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==",
+ "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.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "events-to-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz",
+ "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=",
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ },
+ "dependencies": {
+ "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"
+ }
+ }
+ }
+ },
+ "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": "3.1.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
+ "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "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": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-entry-cache": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+ "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^2.0.1"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-cache-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^2.0.0",
+ "pkg-dir": "^3.0.0"
+ }
+ },
+ "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"
+ }
+ },
+ "findit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz",
+ "integrity": "sha1-ZQnwEmr0wXhVHPqZOU4DLhOk1W4=",
+ "dev": true
+ },
+ "flat-cache": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "dev": true,
+ "requires": {
+ "flatted": "^2.0.0",
+ "rimraf": "2.6.3",
+ "write": "1.0.3"
+ }
+ },
+ "flatted": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
+ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
+ "dev": true
+ },
+ "flow-parser": {
+ "version": "0.121.0",
+ "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.121.0.tgz",
+ "integrity": "sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg==",
+ "dev": true
+ },
+ "flow-remove-types": {
+ "version": "2.121.0",
+ "resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.121.0.tgz",
+ "integrity": "sha512-DbHgYJLD88fMK6CF3Z6wvoZuMb2sqKYP9WLzrZ0SPWbQf61+XyNq6vC8HAJeWJf2DD8z7XhrFHUCH2cJvpAAIQ==",
+ "dev": true,
+ "requires": {
+ "flow-parser": "^0.121.0",
+ "pirates": "^3.0.2",
+ "vlq": "^0.2.1"
+ }
+ },
+ "foreground-child": {
+ "version": "1.5.6",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz",
+ "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^4",
+ "signal-exit": "^3.0.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
+ "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "which": "^1.2.9"
+ }
+ }
+ }
+ },
+ "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-exists-cached": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz",
+ "integrity": "sha1-zyVVTKBQ3EmuZla0HeQiWJidy84=",
+ "dev": true
+ },
+ "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": "2.1.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
+ "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
+ "dev": true,
+ "optional": true
+ },
+ "function-loop": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-1.0.2.tgz",
+ "integrity": "sha512-Iw4MzMfS3udk/rqxTiDDCllhGwlOrsr50zViTOO/W6lS/9y6B1J0BD2VZzrnWUYBJsl3aeqjgR5v7bWWhZSYbA==",
+ "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
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "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"
+ }
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "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"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "12.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+ "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+ "dev": true
+ },
+ "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-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "hasha": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz",
+ "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=",
+ "dev": true,
+ "requires": {
+ "is-stream": "^1.0.1"
+ }
+ },
+ "hosted-git-info": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+ "dev": true
+ },
+ "html-escaper": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.1.tgz",
+ "integrity": "sha512-hNX23TjWwD3q56HpWjUHOKj1+4KKlnjv9PcmBUYKVpga+2cnb9nDx/B1o0yO4n+RZXZdiNxzx6B24C9aNMTkkQ==",
+ "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.5.1",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.1.tgz",
+ "integrity": "sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q==",
+ "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.2.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
+ "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+ "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
+ },
+ "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.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "inquirer": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz",
+ "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^3.0.0",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.15",
+ "mute-stream": "0.0.8",
+ "run-async": "^2.4.0",
+ "rxjs": "^6.5.3",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "through": "^2.3.6"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^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-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "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-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "istanbul-lib-coverage": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
+ "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
+ "dev": true
+ },
+ "istanbul-lib-hook": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz",
+ "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==",
+ "dev": true,
+ "requires": {
+ "append-transform": "^1.0.0"
+ }
+ },
+ "istanbul-lib-instrument": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz",
+ "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==",
+ "dev": true,
+ "requires": {
+ "@babel/generator": "^7.4.0",
+ "@babel/parser": "^7.4.3",
+ "@babel/template": "^7.4.0",
+ "@babel/traverse": "^7.4.3",
+ "@babel/types": "^7.4.0",
+ "istanbul-lib-coverage": "^2.0.5",
+ "semver": "^6.0.0"
+ }
+ },
+ "istanbul-lib-processinfo": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-1.0.0.tgz",
+ "integrity": "sha512-FY0cPmWa4WoQNlvB8VOcafiRoB5nB+l2Pz2xGuXHRSy1KM8QFOYfz/rN+bGMCAeejrY3mrpF5oJHcN0s/garCg==",
+ "dev": true,
+ "requires": {
+ "archy": "^1.0.0",
+ "cross-spawn": "^6.0.5",
+ "istanbul-lib-coverage": "^2.0.3",
+ "rimraf": "^2.6.3",
+ "uuid": "^3.3.2"
+ }
+ },
+ "istanbul-lib-report": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz",
+ "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^2.0.5",
+ "make-dir": "^2.1.0",
+ "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"
+ }
+ }
+ }
+ },
+ "istanbul-lib-source-maps": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz",
+ "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^2.0.5",
+ "make-dir": "^2.1.0",
+ "rimraf": "^2.6.3",
+ "source-map": "^0.6.1"
+ }
+ },
+ "istanbul-reports": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz",
+ "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==",
+ "dev": true,
+ "requires": {
+ "html-escaper": "^2.0.0"
+ }
+ },
+ "jackspeak": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.0.tgz",
+ "integrity": "sha512-VDcSunT+wcccoG46FtzuBAyQKlzhHjli4q31e1fIHGOsRspqNUFjVzGb+7eIFDlTvqLygxapDHPHS0ouT2o/tw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^4.1.0"
+ }
+ },
+ "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.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",
+ "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
+ },
+ "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-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"
+ }
+ },
+ "lcov-parse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz",
+ "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "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"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
+ },
+ "lodash.flattendeep": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
+ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
+ "dev": true
+ },
+ "log-driver": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz",
+ "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==",
+ "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"
+ }
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "merge-source-map": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
+ "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.6.1"
+ }
+ },
+ "mime-db": {
+ "version": "1.43.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
+ "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.26",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
+ "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.43.0"
+ }
+ },
+ "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
+ },
+ "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": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "minipass": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz",
+ "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
+ "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "mysql": {
+ "version": "2.18.1",
+ "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
+ "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
+ "requires": {
+ "bignumber.js": "9.0.0",
+ "readable-stream": "2.3.7",
+ "safe-buffer": "5.1.2",
+ "sqlstring": "2.3.1"
+ }
+ },
+ "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
+ },
+ "nested-error-stacks": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz",
+ "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==",
+ "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-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
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "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
+ },
+ "nyc": {
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz",
+ "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==",
+ "dev": true,
+ "requires": {
+ "archy": "^1.0.0",
+ "caching-transform": "^3.0.2",
+ "convert-source-map": "^1.6.0",
+ "cp-file": "^6.2.0",
+ "find-cache-dir": "^2.1.0",
+ "find-up": "^3.0.0",
+ "foreground-child": "^1.5.6",
+ "glob": "^7.1.3",
+ "istanbul-lib-coverage": "^2.0.5",
+ "istanbul-lib-hook": "^2.0.7",
+ "istanbul-lib-instrument": "^3.3.0",
+ "istanbul-lib-report": "^2.0.8",
+ "istanbul-lib-source-maps": "^3.0.6",
+ "istanbul-reports": "^2.2.4",
+ "js-yaml": "^3.13.1",
+ "make-dir": "^2.1.0",
+ "merge-source-map": "^1.1.0",
+ "resolve-from": "^4.0.0",
+ "rimraf": "^2.6.3",
+ "signal-exit": "^3.0.2",
+ "spawn-wrap": "^1.4.2",
+ "test-exclude": "^5.2.3",
+ "uuid": "^3.3.2",
+ "yargs": "^13.2.2",
+ "yargs-parser": "^13.0.0"
+ }
+ },
+ "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
+ },
+ "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": "5.1.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+ "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "opener": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz",
+ "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==",
+ "dev": true
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "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
+ },
+ "own-or": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz",
+ "integrity": "sha1-Tod/vtqaLsgAD7wLyuOWRe6L+Nw=",
+ "dev": true
+ },
+ "own-or-env": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.1.tgz",
+ "integrity": "sha512-y8qULRbRAlL6x2+M0vIe7jJbJx/kmUTzYonRAa2ayesR2qWLswninkVyeJe4x3IEXhdgoNodzjQRKAoEs6Fmrw==",
+ "dev": true,
+ "requires": {
+ "own-or": "^1.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
+ "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "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
+ },
+ "package-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz",
+ "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.15",
+ "hasha": "^3.0.0",
+ "lodash.flattendeep": "^4.4.0",
+ "release-zalgo": "^1.0.0"
+ }
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "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-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-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": "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"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true
+ }
+ }
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "pirates": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-3.0.2.tgz",
+ "integrity": "sha512-c5CgUJq6H2k6MJz72Ak1F5sN9n9wlSlJyEnwvpm9/y3WB4E3pHBDT2c6PEiS1vyJvq2bUxUAIu0EGf8Cx4Ic7Q==",
+ "dev": true,
+ "requires": {
+ "node-modules-regexp": "^1.0.0"
+ }
+ },
+ "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"
+ }
+ },
+ "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.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
+ "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==",
+ "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
+ },
+ "react": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
+ "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2"
+ }
+ },
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "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"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "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"
+ }
+ },
+ "readdirp": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
+ "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.0.7"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.5",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
+ "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
+ "dev": true
+ },
+ "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
+ },
+ "release-zalgo": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
+ "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=",
+ "dev": true,
+ "requires": {
+ "es6-error": "^4.0.1"
+ }
+ },
+ "request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "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.3",
+ "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.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "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": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
+ "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
+ "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": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.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.4.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
+ "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==",
+ "dev": true,
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
+ "rxjs": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
+ "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+ "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=="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "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.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ },
+ "dependencies": {
+ "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
+ }
+ }
+ },
+ "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-support": {
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
+ "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "spawn-wrap": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz",
+ "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==",
+ "dev": true,
+ "requires": {
+ "foreground-child": "^1.5.6",
+ "mkdirp": "^0.5.0",
+ "os-homedir": "^1.0.1",
+ "rimraf": "^2.6.2",
+ "signal-exit": "^3.0.2",
+ "which": "^1.3.0"
+ }
+ },
+ "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.5",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+ "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
+ "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
+ },
+ "sqlstring": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
+ "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
+ },
+ "sshpk": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "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"
+ }
+ },
+ "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
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.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==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "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"
+ },
+ "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-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+ "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.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ },
+ "dependencies": {
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "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
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ }
+ }
+ },
+ "tap": {
+ "version": "14.10.7",
+ "resolved": "https://registry.npmjs.org/tap/-/tap-14.10.7.tgz",
+ "integrity": "sha512-DVx00lfiMxFhofwFDP77pitRCruVQJn8Dcj/6auIU3dErJQWsKT94oG6Yj0MQRuYANhSec8ruIPyUjH/RI9Hrw==",
+ "dev": true,
+ "requires": {
+ "@types/react": "^16.9.16",
+ "async-hook-domain": "^1.1.3",
+ "bind-obj-methods": "^2.0.0",
+ "browser-process-hrtime": "^1.0.0",
+ "chokidar": "^3.3.0",
+ "color-support": "^1.1.0",
+ "coveralls": "^3.0.8",
+ "diff": "^4.0.1",
+ "esm": "^3.2.25",
+ "findit": "^2.0.0",
+ "flow-remove-types": "^2.112.0",
+ "foreground-child": "^1.3.3",
+ "fs-exists-cached": "^1.0.0",
+ "function-loop": "^1.0.2",
+ "glob": "^7.1.6",
+ "import-jsx": "^3.1.0",
+ "ink": "^2.6.0",
+ "isexe": "^2.0.0",
+ "istanbul-lib-processinfo": "^1.0.0",
+ "jackspeak": "^1.4.0",
+ "minipass": "^3.1.1",
+ "mkdirp": "^0.5.1",
+ "nyc": "^14.1.1",
+ "opener": "^1.5.1",
+ "own-or": "^1.0.0",
+ "own-or-env": "^1.0.1",
+ "react": "^16.12.0",
+ "rimraf": "^2.7.1",
+ "signal-exit": "^3.0.0",
+ "source-map-support": "^0.5.16",
+ "stack-utils": "^1.0.2",
+ "tap-mocha-reporter": "^5.0.0",
+ "tap-parser": "^10.0.1",
+ "tap-yaml": "^1.0.0",
+ "tcompare": "^3.0.0",
+ "treport": "^1.0.2",
+ "trivial-deferred": "^1.0.1",
+ "ts-node": "^8.5.2",
+ "typescript": "^3.7.2",
+ "which": "^2.0.2",
+ "write-file-atomic": "^3.0.1",
+ "yaml": "^1.7.2",
+ "yapool": "^1.0.0"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.3"
+ }
+ },
+ "@babel/core": {
+ "version": "7.8.7",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.8.7",
+ "@babel/helpers": "^7.8.4",
+ "@babel/parser": "^7.8.7",
+ "@babel/template": "^7.8.6",
+ "@babel/traverse": "^7.8.6",
+ "@babel/types": "^7.8.7",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.1",
+ "json5": "^2.1.0",
+ "lodash": "^4.17.13",
+ "resolve": "^1.3.2",
+ "semver": "^5.4.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.8.8",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.7",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.13",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-builder-react-jsx": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3",
+ "esutils": "^2.0.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.8.3",
+ "@babel/template": "^7.8.3",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helpers": {
+ "version": "7.8.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.8.3",
+ "@babel/traverse": "^7.8.4",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.0",
+ "esutils": "^2.0.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.8.8",
+ "bundled": true,
+ "dev": true
+ },
+ "@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-jsx": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-transform-destructuring": {
+ "version": "7.8.8",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-transform-react-jsx": {
+ "version": "7.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/helper-builder-react-jsx": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-jsx": "^7.8.3"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.8.7",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "@babel/template": {
+ "version": "7.8.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.8.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.8.6",
+ "@babel/helper-function-name": "^7.8.3",
+ "@babel/helper-split-export-declaration": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/types": {
+ "version": "7.8.7",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.13",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@types/color-name": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "@types/prop-types": {
+ "version": "15.7.3",
+ "bundled": true,
+ "dev": true
+ },
+ "@types/react": {
+ "version": "16.9.23",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "csstype": "^2.2.0"
+ }
+ },
+ "@types/yoga-layout": {
+ "version": "1.9.1",
+ "bundled": true,
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "4.3.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.11.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "ansicolors": {
+ "version": "0.3.2",
+ "bundled": true,
+ "dev": true
+ },
+ "arrify": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "astral-regex": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "auto-bind": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "caller-callsite": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "callsites": "^2.0.0"
+ }
+ },
+ "caller-path": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "caller-callsite": "^2.0.0"
+ }
+ },
+ "callsites": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "cardinal": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansicolors": "~0.3.2",
+ "redeyed": "~2.1.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "ci-info": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-truncate": {
+ "version": "2.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "slice-ansi": "^3.0.0",
+ "string-width": "^4.2.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.7.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "csstype": {
+ "version": "2.6.9",
+ "bundled": true,
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "bundled": true,
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "events-to-array": {
+ "version": "1.1.2",
+ "bundled": true,
+ "dev": true
+ },
+ "gensync": {
+ "version": "1.0.0-beta.1",
+ "bundled": true,
+ "dev": true
+ },
+ "globals": {
+ "version": "11.12.0",
+ "bundled": true,
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "import-jsx": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.5.5",
+ "@babel/plugin-proposal-object-rest-spread": "^7.5.5",
+ "@babel/plugin-transform-destructuring": "^7.5.0",
+ "@babel/plugin-transform-react-jsx": "^7.3.0",
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "ink": {
+ "version": "2.7.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "arrify": "^2.0.1",
+ "auto-bind": "^4.0.0",
+ "chalk": "^3.0.0",
+ "cli-cursor": "^3.1.0",
+ "cli-truncate": "^2.1.0",
+ "is-ci": "^2.0.0",
+ "lodash.throttle": "^4.1.1",
+ "log-update": "^3.0.0",
+ "prop-types": "^15.6.2",
+ "react-reconciler": "^0.24.0",
+ "scheduler": "^0.18.0",
+ "signal-exit": "^3.0.2",
+ "slice-ansi": "^3.0.0",
+ "string-length": "^3.1.0",
+ "widest-line": "^3.1.0",
+ "wrap-ansi": "^6.2.0",
+ "yoga-layout-prebuilt": "^1.9.3"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ci-info": "^2.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "bundled": true,
+ "dev": true
+ },
+ "json5": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "lodash": {
+ "version": "4.17.15",
+ "bundled": true,
+ "dev": true
+ },
+ "lodash.throttle": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "log-update": {
+ "version": "3.4.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.2.0",
+ "cli-cursor": "^2.1.0",
+ "wrap-ansi": "^5.0.0"
+ },
+ "dependencies": {
+ "ansi-escapes": {
+ "version": "3.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "2.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "mimic-fn": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ }
+ }
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "bundled": true,
+ "dev": true
+ },
+ "minipass": {
+ "version": "3.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "yallist": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "onetime": {
+ "version": "5.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "bundled": true,
+ "dev": true
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "react-is": {
+ "version": "16.13.1",
+ "bundled": true,
+ "dev": true
+ },
+ "react-reconciler": {
+ "version": "0.24.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2",
+ "scheduler": "^0.18.0"
+ }
+ },
+ "redeyed": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "esprima": "~4.0.0"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.5",
+ "bundled": true,
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.15.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "scheduler": {
+ "version": "0.18.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "bundled": true,
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "string-length": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "astral-regex": "^1.0.0",
+ "strip-ansi": "^5.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "tap-parser": {
+ "version": "10.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "events-to-array": "^1.0.1",
+ "minipass": "^3.0.0",
+ "tap-yaml": "^1.0.0"
+ }
+ },
+ "tap-yaml": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yaml": "^1.5.0"
+ }
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "treport": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cardinal": "^2.1.1",
+ "chalk": "^3.0.0",
+ "import-jsx": "^3.1.0",
+ "ink": "^2.6.0",
+ "ms": "^2.1.2",
+ "string-length": "^3.1.0",
+ "tap-parser": "^10.0.1",
+ "unicode-length": "^2.0.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "type-fest": {
+ "version": "0.11.0",
+ "bundled": true,
+ "dev": true
+ },
+ "unicode-length": {
+ "version": "2.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "punycode": "^2.0.0",
+ "strip-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "widest-line": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "string-width": "^4.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "yaml": {
+ "version": "1.8.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.8.7"
+ }
+ },
+ "yoga-layout-prebuilt": {
+ "version": "1.9.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@types/yoga-layout": "1.9.1"
+ }
+ }
+ }
+ },
+ "tap-mocha-reporter": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.1.tgz",
+ "integrity": "sha512-1knFWOwd4khx/7uSEnUeaP9IPW3w+sqTgJMhrwah6t46nZ8P25atOKAjSvVDsT67lOPu0nfdOqUwoyKn+3E5pA==",
+ "dev": true,
+ "requires": {
+ "color-support": "^1.1.0",
+ "debug": "^4.1.1",
+ "diff": "^4.0.1",
+ "escape-string-regexp": "^2.0.0",
+ "glob": "^7.0.5",
+ "tap-parser": "^10.0.0",
+ "tap-yaml": "^1.0.0",
+ "unicode-length": "^2.0.2"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true
+ }
+ }
+ },
+ "tap-parser": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-10.0.1.tgz",
+ "integrity": "sha512-qdT15H0DoJIi7zOqVXDn9X0gSM68JjNy1w3VemwTJlDnETjbi6SutnqmBfjDJAwkFS79NJ97gZKqie00ZCGmzg==",
+ "dev": true,
+ "requires": {
+ "events-to-array": "^1.0.1",
+ "minipass": "^3.0.0",
+ "tap-yaml": "^1.0.0"
+ }
+ },
+ "tap-yaml": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.0.tgz",
+ "integrity": "sha512-Rxbx4EnrWkYk0/ztcm5u3/VznbyFJpyXO12dDBHKWiDVxy7O2Qw6MRrwO5H6Ww0U5YhRY/4C/VzWmFPhBQc4qQ==",
+ "dev": true,
+ "requires": {
+ "yaml": "^1.5.0"
+ }
+ },
+ "tcompare": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-3.0.4.tgz",
+ "integrity": "sha512-Q3TitMVK59NyKgQyFh+857wTAUE329IzLDehuPgU4nF5e8g+EUQ+yUbjUy1/6ugiNnXztphT+NnqlCXolv9P3A==",
+ "dev": true,
+ "requires": {
+ "diff-frag": "^1.0.1"
+ }
+ },
+ "test-exclude": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz",
+ "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3",
+ "minimatch": "^3.0.4",
+ "read-pkg-up": "^4.0.0",
+ "require-main-filename": "^2.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
+ },
+ "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"
+ }
+ },
+ "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-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "trivial-deferred": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.0.1.tgz",
+ "integrity": "sha1-N21NKdlR1jaKb3oK6FwvTV4GWPM=",
+ "dev": true
+ },
+ "ts-node": {
+ "version": "8.8.1",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.8.1.tgz",
+ "integrity": "sha512-10DE9ONho06QORKAaCBpPiFCdW+tZJuY/84tyypGtl6r+/C7Asq0dhqbRZURuUlLQtZxxDvT8eoj8cGW0ha6Bg==",
+ "dev": true,
+ "requires": {
+ "arg": "^4.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "source-map-support": "^0.5.6",
+ "yn": "3.1.1"
+ }
+ },
+ "tslib": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
+ "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
+ "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"
+ }
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ },
+ "typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "dev": true,
+ "requires": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "typescript": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
+ "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
+ "dev": true
+ },
+ "unicode-length": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.0.2.tgz",
+ "integrity": "sha512-Ph/j1VbS3/r77nhoY2WU0GWGjVYOHL3xpKp0y/Eq2e5r0mT/6b649vm7KFO6RdAdrZkYLdxphYVgvODxPB+Ebg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.0.0",
+ "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
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "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="
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ },
+ "v8-compile-cache": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
+ "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
+ "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"
+ }
+ },
+ "vlq": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz",
+ "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "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",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+ "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ },
+ "write-file-atomic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "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
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "dev": true
+ },
+ "yaml": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.8.3.tgz",
+ "integrity": "sha512-X/v7VDnK+sxbQ2Imq4Jt2PRUsRsP7UcpSl3Llg6+NRRqWLIvxkMFYtH1FmvwNGYRKKPa+EPA4qDBlI9WVG1UKw==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.8.7"
+ }
+ },
+ "yapool": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/yapool/-/yapool-1.0.0.tgz",
+ "integrity": "sha1-9pPymjFbUNmp2iZGp6ZkXJaYW2o=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ },
+ "dependencies": {
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "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
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
index 6b100c0f..0c2ee900 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,13 @@
{
"name": "zongji",
- "version": "0.4.5",
+ "version": "0.5.1",
"description": "A mysql binlog listener running on Node.js",
"main": "index.js",
"directories": {
"test": "test"
},
"scripts": {
- "test": "nodeunit --reporter=minimal test",
+ "test": "tap --bail --no-coverage --jobs=1 test/*.js",
"lint": "eslint ."
},
"repository": {
@@ -28,12 +28,16 @@
"url": "https://github.com/nevill/zongji/issues"
},
"homepage": "https://github.com/nevill/zongji",
+ "engines": {
+ "node": ">= 8.0"
+ },
"devDependencies": {
- "eslint": "2.13.1",
- "nodeunit": "~0.9.1"
+ "eslint": "6.8.0",
+ "tap": "14.10.7"
},
"dependencies": {
- "iconv-lite": "^0.4.13",
- "mysql": "2.15.0"
+ "big-integer": "1.6.48",
+ "iconv-lite": "0.5.1",
+ "mysql": "2.18.1"
}
}
diff --git a/test/codemap.js b/test/codemap.js
new file mode 100644
index 00000000..91b3639d
--- /dev/null
+++ b/test/codemap.js
@@ -0,0 +1,8 @@
+const tap = require('tap');
+const getEventClass = require('./../lib/code_map').getEventClass;
+
+tap.test('Codemap', test => {
+ test.equal(getEventClass(2).name, 'Query');
+ test.equal(getEventClass(490).name, 'Unknown');
+ test.end();
+});
diff --git a/test/errors.js b/test/errors.js
index 9514bcaf..5df5f990 100644
--- a/test/errors.js
+++ b/test/errors.js
@@ -1,175 +1,188 @@
-var ZongJi = require('./../');
-var getEventClass = require('./../lib/code_map').getEventClass;
-var settings = require('./settings/mysql');
-var connector = require('./helpers/connector');
-var querySequence = require('./helpers/querySequence');
-
-var conn = process.testZongJi || {};
-
-function generateDisconnectionCase(readyKillIdFun, cleanupKillIdFun) {
- return function(test) {
- var zongji = new ZongJi(settings.connection);
- var errorTrapped = false;
- var ACCEPTABLE_ERRORS = [
- 'PROTOCOL_CONNECTION_LOST',
- // MySQL 5.1 emits a packet sequence error when the binlog disconnected
- 'PROTOCOL_INCORRECT_PACKET_SEQUENCE'
- ];
-
- zongji.on('error', function(error) {
- if (!errorTrapped && ACCEPTABLE_ERRORS.indexOf(error.code) > -1) {
- errorTrapped = true;
- killThread(cleanupKillIdFun);
- test.done();
+const tap = require('tap');
+
+const ZongJi = require('../');
+const settings = require('./settings/mysql');
+const testDb = require('./helpers');
+
+tap.test('Connect to an invalid host', test => {
+ const zongji = new ZongJi({
+ host: 'wronghost',
+ user: 'wronguser',
+ password: 'wrongpass'
+ });
+
+ zongji.on('error', function(error) {
+ test.ok(['ENOTFOUND', 'ETIMEDOUT'].indexOf(error.code) !== -1);
+ test.end();
+ });
+
+ test.tearDown(() => zongji.stop());
+ zongji.start();
+});
+
+tap.test('Initialise testing db', test => {
+ testDb.init(err => {
+ if (err) {
+ return test.threw(err);
+ }
+ test.end();
+ });
+});
+
+const ACCEPTABLE_ERRORS = [
+ 'PROTOCOL_CONNECTION_LOST',
+ // MySQL 5.1 emits a packet sequence error when the binlog disconnected
+ 'PROTOCOL_INCORRECT_PACKET_SEQUENCE'
+];
+
+tap.test('Disconnect binlog connection', test => {
+ const zongji = new ZongJi(settings.connection);
+
+ zongji.start({
+ includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
+ serverId: testDb.serverId(),
+ });
+
+ zongji.on('ready', () => {
+ let threadId = zongji.connection.threadId;
+ test.ok(!isNaN(threadId));
+ testDb.execute([`kill ${threadId}`], err => {
+ if (err) {
+ test.threw(err);
}
});
+ });
- zongji.start({
- includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
- // Anything other than default (1) as used in helpers/connector
- serverId: 12
+ zongji.on('error', err => {
+ if (ACCEPTABLE_ERRORS.indexOf(err.code) > -1) {
+ zongji.stop();
+ test.end();
+ } else {
+ test.threw(err);
+ }
+ });
+});
+
+tap.test('Disconnect control connection', test => {
+ const zongji = new ZongJi(settings.connection);
+
+ zongji.start({
+ includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
+ serverId: testDb.serverId(),
+ });
+
+ zongji.on('ready', () => {
+ let threadId = zongji.ctrlConnection.threadId;
+ test.ok(!isNaN(threadId));
+ testDb.execute([`kill ${threadId}`], err => {
+ if (err) {
+ test.threw(err);
+ }
});
+ });
- function killThread(argFun) {
- var threadId = argFun(zongji);
- test.ok(!isNaN(threadId));
- conn.db.query('KILL ' + threadId);
+ zongji.on('error', err => {
+ if (ACCEPTABLE_ERRORS.indexOf(err.code) > -1) {
+ zongji.stop();
+ test.end();
+ } else {
+ test.threw(err);
}
+ });
+});
+
+
+tap.test('Events come through in sequence', test => {
+ const NEW_INST_TIMEOUT = 1000;
+ const UPDATE_INTERVAL = 300;
+ const UPDATE_COUNT = 5;
+ const TEST_TABLE = 'reconnect_at_pos';
+
+ test.test(`prepare table ${TEST_TABLE}`, test => {
+ testDb.execute([
+ `DROP TABLE IF EXISTS ${TEST_TABLE}`,
+ `CREATE TABLE ${TEST_TABLE} (col INT UNSIGNED)`,
+ `INSERT INTO ${TEST_TABLE} (col) VALUES (10)`,
+ ], err =>{
+ if (err) {
+ return test.threw(err);
+ }
+ test.end();
+ });
+ });
+
+ test.test('when reconnect', test => {
+ const result = [];
+
+ function startPeriodicallyWriting() {
+ let sequences = Array.from(
+ {length: UPDATE_COUNT},
+ (_, i) => `INSERT INTO ${TEST_TABLE} (col) VALUES (${i})`
+ );
+
+ let updateInterval = setInterval(() => {
+ testDb.execute([sequences.shift()], error => {
+ if (error) {
+ clearInterval(updateInterval);
+ test.threw(error);
+ }
+ });
- function isZongjiReady() {
- setTimeout(function() {
- if (zongji.ready) {
- killThread(readyKillIdFun);
- } else {
- isZongjiReady();
+ if (sequences.length === 0) {
+ clearInterval(updateInterval);
}
- }, 100);
+ }, UPDATE_INTERVAL);
}
- isZongjiReady();
- };
-}
+ function newInstance(options) {
+ const zongji = new ZongJi(settings.connection);
+
+ zongji.start({
+ ...options,
+ // Must include rotate events for filename and position properties
+ includeEvents: [
+ 'rotate', 'tablemap', 'writerows', 'updaterows', 'deleterows'
+ ]
+ });
-module.exports = {
- setUp: function(done) {
- if (!conn.db) {
- process.testZongJi = connector.call(conn, settings, done);
- } else {
- conn.incCount();
- done();
- }
- },
- tearDown: function(done) {
- if (conn) {
- conn.eventLog.splice(0, conn.eventLog.length);
- conn.errorLog.splice(0, conn.errorLog.length);
- conn.closeIfInactive(1000);
- }
- done();
- },
- binlogConnection_disconnect: generateDisconnectionCase(
- function onReady(zongji) { return zongji.connection.threadId; },
- function onCleanup(zongji) { return zongji.ctrlConnection.threadId; }),
- ctrlConnection_disconnect: generateDisconnectionCase(
- function onReady(zongji) { return zongji.ctrlConnection.threadId; },
- function onCleanup(zongji) { return zongji.connection.threadId; }),
-
- reconnect_at_pos: function(test) {
- // Test that binlog events come through in correct sequence after
- // reconnect using the binlogName and binlogNextPos properties
- var NEW_INST_TIMEOUT = 1000;
- var UPDATE_INTERVAL = 300;
- var UPDATE_COUNT = 5;
- var TEST_TABLE = 'reconnect_at_pos';
-
- var updatesSent = 0, updateEvents = 0;
-
- // Create a new ZongJi instance using some default options that will count
- // using the values in the new rows inserted
- function startNewZongJi(options) {
- var zongji = new ZongJi(settings.connection);
-
- zongji.start(Object.keys(options || {}).reduce(function(opts, setKey) {
- // Object.assign-like to support node 0.10
- opts[setKey] = options[setKey];
- return opts;
- }, {
- // Must include rotate events for binlogName and binlogNextPos properties
- includeEvents: ['rotate', 'tablemap', 'writerows', 'updaterows', 'deleterows']
- }));
zongji.on('binlog', function(event) {
if (event.getTypeName() === 'WriteRows') {
- if (updateEvents++ !== event.rows[0].col) {
- exitTest('Events in the wrong order');
- } else if (updateEvents === UPDATE_COUNT) {
- exitTest();
- }
+ result.push(event.rows[0].col);
+ }
+
+ if (result.length === UPDATE_COUNT) {
+ test.strictSame(
+ result,
+ Array.from({length: UPDATE_COUNT}, (_, i) => i)
+ );
+ test.end();
}
});
+
return zongji;
}
- var firstZongJi;
- var secondZongJi;
-
- querySequence(conn.db, [
- 'DROP TABLE IF EXISTS ' + conn.escId(TEST_TABLE),
- 'CREATE TABLE ' + conn.escId(TEST_TABLE) + ' (col INT UNSIGNED)',
- 'INSERT INTO ' + conn.escId(TEST_TABLE) + ' (col) VALUES (10)',
- ], function(error) {
- if (error)
- return exitTest(error);
+ let first = newInstance({
+ serverId: testDb.serverId(),
+ startAtEnd: true,
+ });
- firstZongJi = startNewZongJi({
- serverId: 14,
- startAtEnd: true
- });
+ first.on('ready', () => {
+ startPeriodicallyWriting();
- setTimeout(function() {
+ first.on('stopped', () => {
// Start new ZongJi instance where the previous was when stopped
- firstZongJi.stop();
- secondZongJi = startNewZongJi({
- serverId: 16,
- binlogName: firstZongJi.binlogName,
- binlogNextPos: firstZongJi.binlogNextPos
+ let second = newInstance({
+ serverId: testDb.serverId(),
+ filename: first.get('filename'),
+ position: first.get('position'),
});
- }, NEW_INST_TIMEOUT);
+ test.tearDown(() => second.stop());
+ });
+ setTimeout(() => first.stop(), NEW_INST_TIMEOUT);
});
+ });
- function exitTest(error) {
- test.ifError(error);
- firstZongJi.stop && firstZongJi.stop();
- secondZongJi.stop && secondZongJi.stop();
- test.done();
- }
-
- var updateInterval = setInterval(function() {
- if (updatesSent++ < UPDATE_COUNT) {
- querySequence(conn.db, [
- 'INSERT INTO ' + conn.escId(TEST_TABLE) + ' (col) VALUES (' + updateEvents + ')',
- ], function(error) { error && exitTest(error); });
- } else {
- clearInterval(updateInterval);
- }
- }, UPDATE_INTERVAL);
-
- },
-
- invalid_host: function(test) {
- var zongji = new ZongJi({
- host: 'wronghost',
- user: 'wronguser',
- password: 'wrongpass'
- });
- zongji.on('error', function(error) {
- test.ok([ 'ENOTFOUND', 'ETIMEDOUT' ].indexOf(error.code) !== -1);
- test.done();
- });
- },
- code_map: function(test) {
- test.equal(getEventClass(2).name, 'Query');
- test.equal(getEventClass(490).name, 'Unknown');
- test.done();
- }
-};
+ test.end();
+});
diff --git a/test/events.js b/test/events.js
index 26bdad2c..c403d88d 100644
--- a/test/events.js
+++ b/test/events.js
@@ -1,282 +1,413 @@
-var mysql = require('mysql');
-var settings = require('./settings/mysql');
-var connector = require('./helpers/connector');
-var querySequence = require('./helpers/querySequence');
-var expectEvents = require('./helpers/expectEvents');
-var ZongJi = require('./../');
+const tap = require('tap');
-var conn = process.testZongJi || {};
+const ZongJi = require('../');
+const expectEvents = require('./helpers/expectEvents');
+const testDb = require('./helpers');
+const settings = require('./settings/mysql');
-var checkTableMatches = function(tableName) {
+const checkTableMatches = function(tableName) {
return function(test, event) {
- var tableDetails = event.tableMap[event.tableId];
- test.strictEqual(tableDetails.parentSchema, settings.database);
+ const tableDetails = event.tableMap[event.tableId];
+ test.strictEqual(tableDetails.parentSchema, testDb.SCHEMA_NAME);
test.strictEqual(tableDetails.tableName, tableName);
};
};
// For use with expectEvents()
-var tableMapEvent = function(tableName) {
+const tableMapEvent = function(tableName) {
return {
_type: 'TableMap',
tableName: tableName,
- schemaName: settings.database
+ schemaName: testDb.SCHEMA_NAME,
};
};
-module.exports = {
- setUp: function(done) {
- if (!conn.db) {
- process.testZongJi = connector.call(conn, settings, done);
- } else {
- conn.incCount();
- done();
+tap.test('Initialise testing db', test => {
+ testDb.init(err => {
+ if (err) {
+ return test.threw(err);
}
- },
- tearDown: function(done) {
- if (conn) {
- conn.eventLog.splice(0, conn.eventLog.length);
- conn.errorLog.splice(0, conn.errorLog.length);
- conn.closeIfInactive(1000);
- }
- done();
- },
- testStartAtEnd: function(test) {
- var testTable = 'start_at_end_test';
- querySequence(conn.db, [
- 'FLUSH LOGS', // Ensure Zongji perserveres through a rotation event
- 'DROP TABLE IF EXISTS ' + conn.escId(testTable),
- 'CREATE TABLE ' + conn.escId(testTable) + ' (col INT UNSIGNED)',
- 'INSERT INTO ' + conn.escId(testTable) + ' (col) VALUES (10)',
- ], function(error) {
- if (error) console.error(error);
- // Start second ZongJi instance
- var zongji = new ZongJi(settings.connection);
- var events = [];
-
- zongji.on('binlog', function(event) {
- events.push(event);
- });
+ test.end();
+ });
+});
+
+tap.test('Binlog option startAtEnd', test => {
+ const TEST_TABLE = 'start_at_end_test';
+
+ test.test(`prepare new table ${TEST_TABLE}`, test => {
+ testDb.execute([
+ 'FLUSH LOGS', // Ensure ZongJi perserveres through a rotation event
+ `DROP TABLE IF EXISTS ${TEST_TABLE}`,
+ `CREATE TABLE ${TEST_TABLE} (col INT UNSIGNED)`,
+ `INSERT INTO ${TEST_TABLE} (col) VALUES (12)`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
+ test.end();
+ });
+ });
- zongji.start({
- startAtEnd: true,
- serverId: 10, // Second instance must not use same server ID
- includeEvents: ['tablemap', 'writerows']
+ test.test('start', test => {
+ const events = [];
+
+ const zongji = new ZongJi(settings.connection);
+ test.tearDown(() => zongji.stop());
+
+ zongji.on('binlog', evt => events.push(evt));
+ zongji.start({
+ startAtEnd: true,
+ includeEvents: ['tablemap', 'writerows'],
+ });
+
+ zongji.on('ready', () => {
+ testDb.execute([
+ `INSERT INTO ${TEST_TABLE} (col) VALUES (9)`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
+
+ // Should only have 2 events since ZongJi start
+ expectEvents(test, events,
+ [
+ { /* do not bother testing anything on first event */ },
+ { rows: [ { col: 9 } ] }
+ ], 1,
+ () => test.end()
+ );
});
+ });
+
+
+ });
+
+ test.end();
+});
+
+tap.test('Class constructor', test => {
+ const TEST_TABLE = 'conn_obj_test';
+
+ test.test(`prepare table ${TEST_TABLE}`, test => {
+ testDb.execute([
+ `DROP TABLE IF EXISTS ${TEST_TABLE}`,
+ `CREATE TABLE ${TEST_TABLE} (col INT UNSIGNED)`,
+ `INSERT INTO ${TEST_TABLE} (col) VALUES (10)`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
+
+ test.end();
+ });
+ });
+
+ function run(test, zongji) {
+ test.tearDown(() => zongji.stop());
- // Give enough time to initialize
- setTimeout(function() {
- querySequence(conn.db, [
- 'INSERT INTO ' + conn.escId(testTable) + ' (col) VALUES (10)',
- ], function(error) {
- if (error) console.error(error);
+ const events = [];
+ zongji.on('binlog', evt => events.push(evt));
+ zongji.start({
+ startAtEnd: true,
+ serverId: testDb.serverId(),
+ includeEvents: ['tablemap', 'writerows'],
+ });
+ zongji.on('ready', () => {
+ let value = Math.round(Math.random() * 100);
+ testDb.execute([
+ `INSERT INTO ${TEST_TABLE} (col) VALUES (${value})`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
// Should only have 2 events since ZongJi start
+
expectEvents(test, events, [
{ /* do not bother testing anything on first event */ },
- { rows: [ { col: 10 } ] }
- ], 1, function() {
- zongji.stop();
- test.done();
- });
+ { rows: [ { col: value } ] }
+ ], 1, () => test.end());
});
- }, 200);
+ });
+ }
+
+ const mysql = require('mysql');
+
+ test.test('pass a mysql connection instance', test => {
+ const conn = mysql.createConnection(settings.connection);
+ const zongji = new ZongJi(conn);
+ zongji.on('stopped', () => conn.destroy());
+ run(test, zongji);
+ });
+
+ test.test('pass a mysql pool', test => {
+ const pool = mysql.createConnection(settings.connection);
+ const zongji = new ZongJi(pool);
+ zongji.on('stopped', () => pool.end());
+ run(test, zongji);
+ });
+
+ test.end();
+});
+
+tap.test('Write events', test => {
+ const TEST_TABLE = 'write_events_test';
+ test.test(`prepare table ${TEST_TABLE}`, test => {
+ testDb.execute([
+ `DROP TABLE IF EXISTS ${TEST_TABLE}`,
+ `CREATE TABLE ${TEST_TABLE} (col INT UNSIGNED)`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
+
+ test.end();
});
- },
- testPassedConnectionObj: function(test) {
- var testTable = 'conn_obj_test';
- var connObjs = [
- { create: mysql.createConnection, end: function(obj) { obj.destroy(); } },
- { create: mysql.createPool, end: function(obj) { obj.end(); } }
- ];
- querySequence(conn.db, [
- 'DROP TABLE IF EXISTS ' + conn.escId(testTable),
- 'CREATE TABLE ' + conn.escId(testTable) + ' (col INT UNSIGNED)',
- 'INSERT INTO ' + conn.escId(testTable) + ' (col) VALUES (10)',
- ], function(error) {
- if (error) console.error(error);
- // Start second ZongJi instance
- connObjs.forEach(function(connObj, index) {
- var ctrlConn = connObj.create(settings.connection);
- var zongji = new ZongJi(ctrlConn);
- var events = [];
-
- zongji.on('binlog', function(event) {
- events.push(event);
- });
+ });
- zongji.start({
- startAtEnd: true,
- serverId: 12 + index, // Second instance must not use same server ID
- includeEvents: ['tablemap', 'writerows']
- });
+ test.test('write a record', test => {
+ const events = [];
+ const zongji = new ZongJi(settings.connection);
+ test.tearDown(() => zongji.stop());
+
+ zongji.start({
+ startAtEnd: true,
+ serverId: testDb.serverId(),
+ includeEvents: ['tablemap', 'writerows'],
+ });
- connObj.zongji = zongji;
- connObj.events = events;
+ zongji.on('ready', () => {
+ testDb.execute([
+ `INSERT INTO ${TEST_TABLE} (col) VALUES (14)`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
});
+ });
- // Give enough time to initialize
- setTimeout(function() {
- querySequence(conn.db, [
- 'INSERT INTO ' + conn.escId(testTable) + ' (col) VALUES (10)',
- ], function(error) {
- if (error) console.error(error);
- // Should only have 2 events since ZongJi start
- var finishedCount = 0;
- connObjs.forEach(function(connObj) {
- expectEvents(test, connObj.events, [
- { /* do not bother testing anything on first event */ },
- { rows: [ { col: 10 } ] }
- ], 1, function() {
- connObj.zongji.stop();
- // When passing connection object, connection doesn't end on stop
- connObj.end(connObj.zongji.ctrlConnection);
- if (++finishedCount === connObjs.length - 1) test.done();
- });
- });
- });
- }, 200);
+ zongji.on('binlog', evt => {
+ events.push(evt);
+ if (events.length == 2) {
+ expectEvents(test, events,
+ [
+ tableMapEvent(TEST_TABLE),
+ {
+ _type: 'WriteRows',
+ _checkTableMap: checkTableMatches(TEST_TABLE),
+ rows: [ { col: 14 } ],
+ }
+ ], 1,
+ () => test.end()
+ );
+ }
});
- },
- testWriteUpdateDelete: function(test) {
- var testTable = 'events_test';
- querySequence(conn.db, [
- 'DROP TABLE IF EXISTS ' + conn.escId(testTable),
- 'CREATE TABLE ' + conn.escId(testTable) + ' (col INT UNSIGNED)',
- 'INSERT INTO ' + conn.escId(testTable) + ' (col) VALUES (10)',
- 'UPDATE ' + conn.escId(testTable) + ' SET col = 15',
- 'DELETE FROM ' + conn.escId(testTable)
- ], function(error) {
- if (error) console.error(error);
- expectEvents(test, conn.eventLog, [
- tableMapEvent(testTable),
- {
- _type: 'WriteRows',
- _checkTableMap: checkTableMatches(testTable),
- rows: [ { col: 10 } ]
- },
- tableMapEvent(testTable),
- {
- _type: 'UpdateRows',
- _checkTableMap: checkTableMatches(testTable),
- rows: [ { before: { col: 10 }, after: { col: 15 } } ]
- },
- tableMapEvent(testTable),
- {
- _type: 'DeleteRows',
- _checkTableMap: checkTableMatches(testTable),
- rows: [ { col: 15 } ]
+ });
+
+ test.test('update a record', test => {
+ const events = [];
+ const zongji = new ZongJi(settings.connection);
+ test.tearDown(() => zongji.stop());
+
+ zongji.start({
+ startAtEnd: true,
+ serverId: testDb.serverId(),
+ includeEvents: ['tablemap', 'updaterows'],
+ });
+
+ zongji.on('ready', () => {
+ testDb.execute([
+ `UPDATE ${TEST_TABLE} SET col=15`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
}
- ], 1, function() {
- test.equal(conn.errorLog.length, 0);
- test.done();
});
});
- },
- testManyColumns: function(test) {
- var testTable = '33_columns';
- querySequence(conn.db, [
- 'DROP TABLE IF EXISTS ' + conn.escId(testTable),
- 'CREATE TABLE ' + conn.escId(testTable) + ' (' +
- 'col1 INT SIGNED NULL, ' +
- 'col2 BIGINT SIGNED NULL, ' +
- 'col3 TINYINT SIGNED NULL, ' +
- 'col4 SMALLINT SIGNED NULL, ' +
- 'col5 MEDIUMINT SIGNED NULL, ' +
- 'col6 INT SIGNED NULL, ' +
- 'col7 BIGINT SIGNED NULL, ' +
- 'col8 TINYINT SIGNED NULL, ' +
- 'col9 SMALLINT SIGNED NULL, ' +
- 'col10 INT SIGNED NULL, ' +
- 'col11 BIGINT SIGNED NULL, ' +
- 'col12 TINYINT SIGNED NULL, ' +
- 'col13 SMALLINT SIGNED NULL, ' +
- 'col14 INT SIGNED NULL, ' +
- 'col15 BIGINT SIGNED NULL, ' +
- 'col16 TINYINT SIGNED NULL, ' +
- 'col17 SMALLINT SIGNED NULL, ' +
- 'col18 INT SIGNED NULL, ' +
- 'col19 BIGINT SIGNED NULL, ' +
- 'col20 TINYINT SIGNED NULL, ' +
- 'col21 SMALLINT SIGNED NULL, ' +
- 'col22 INT SIGNED NULL, ' +
- 'col23 BIGINT SIGNED NULL, ' +
- 'col24 TINYINT SIGNED NULL, ' +
- 'col25 SMALLINT SIGNED NULL, ' +
- 'col26 INT SIGNED NULL, ' +
- 'col27 BIGINT SIGNED NULL, ' +
- 'col28 TINYINT SIGNED NULL, ' +
- 'col29 SMALLINT SIGNED NULL, ' +
- 'col30 INT SIGNED NULL, ' +
- 'col31 BIGINT SIGNED NULL, ' +
- 'col32 TINYINT SIGNED NULL, ' +
- 'col33 SMALLINT SIGNED NULL)',
- 'INSERT INTO ' + conn.escId(testTable) +
- ' (col1, col2, col3, col4, col5, col33) VALUES ' +
- '(2147483647, null, 127, 32767, 8388607, 12), ' +
- '(-2147483648, -9007199254740992, -128, -32768, -8388608, 10), ' +
- '(-2147483645, -9007199254740990, -126, -32766, -8388606, 6), ' +
- '(-1, -1, -1, -1, null, -6), ' +
- '(123456, 100, 96, 300, 1000, null), ' +
- '(-123456, -100, -96, -300, -1000, null)',
- 'SELECT * FROM ' + conn.escId(testTable)
- ], function(error, results) {
- if (error) console.error(error);
- expectEvents(test, conn.eventLog, [
- { /* do not bother testing anything on first event */ },
- { rows: results[results.length - 1] }
- ], 1, test.done);
+
+ zongji.on('binlog', evt => {
+ events.push(evt);
+
+ if (events.length == 2) {
+ expectEvents(test, events,
+ [
+ tableMapEvent(TEST_TABLE),
+ {
+ _type: 'UpdateRows',
+ _checkTableMap: checkTableMatches(TEST_TABLE),
+ rows: [ { before: { col: 14 }, after: { col: 15 } } ],
+ }
+ ], 1,
+ () => test.end()
+ );
+ }
+ });
+ });
+
+ test.test('delete a record', test => {
+ const events = [];
+ const zongji = new ZongJi(settings.connection);
+ test.tearDown(() => zongji.stop());
+
+ zongji.start({
+ startAtEnd: true,
+ serverId: testDb.serverId(),
+ includeEvents: ['tablemap', 'deleterows'],
});
- },
- testIntvar: function(test) {
- var testTable = 'intvar_test';
- querySequence(conn.db, [
- 'DROP TABLE IF EXISTS ' + conn.escId(testTable),
- 'CREATE TABLE ' + conn.escId(testTable) + ' (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY , col INT)',
- ], function(error) {
- if (error) console.error(error);
- // Start second ZongJi instance
- var zongji = new ZongJi(settings.connection);
- var events = [];
-
- zongji.on('binlog', function(event) {
- if (event.getTypeName() === 'Query' && event.query === 'BEGIN')
- return;
- events.push(event);
- });
- zongji.start({
- startAtEnd: true,
- serverId: 12, // Second instance must not use same server ID
- includeEvents: ['intvar', 'query']
+ zongji.on('ready', () => {
+ testDb.execute([
+ `DELETE FROM ${TEST_TABLE}`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
});
+ });
- // Give enough time to initialize
- setTimeout(function() {
- querySequence(conn.db, [
- 'SET SESSION binlog_format=STATEMENT',
- 'INSERT INTO ' + conn.escId(testTable) + ' (col) VALUES (10)',
- 'INSERT INTO ' + conn.escId(testTable) + ' (col) VALUES (11)',
- 'INSERT INTO ' + conn.escId(testTable) + ' (id, col) VALUES (100, LAST_INSERT_ID())',
- // Other tests expect row-based replication, so reset here
- 'SET SESSION binlog_format=ROW',
- ], function(error) {
- if (error) console.error(error);
- expectEvents(test, events, [
+ zongji.on('binlog', evt => {
+ events.push(evt);
+
+ if (events.length == 2) {
+ expectEvents(test, events,
+ [
+ tableMapEvent(TEST_TABLE),
+ {
+ _type: 'DeleteRows',
+ _checkTableMap: checkTableMatches(TEST_TABLE),
+ rows: [ { col: 15 } ],
+ }
+ ], 1,
+ () => test.end()
+ );
+ }
+ });
+ });
+
+ test.end();
+});
+
+tap.test('Intvar / Query event', test => {
+ const TEST_TABLE = 'intvar_test';
+
+ test.test(`prepare table ${TEST_TABLE}`, test => {
+ testDb.execute([
+ `DROP TABLE IF EXISTS ${TEST_TABLE}`,
+ `CREATE TABLE ${TEST_TABLE} (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, col INT)`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
+
+ test.end();
+ });
+ });
+
+ test.test('begin', test => {
+ const events = [];
+ const zongji = new ZongJi(settings.connection);
+ test.tearDown(() => zongji.stop());
+
+ zongji.on('binlog', event => {
+ if (event.getTypeName() === 'Query' && event.query === 'BEGIN') {
+ return;
+ }
+ events.push(event);
+
+ if (events.length === 6) {
+ expectEvents(test, events, [
{ _type: 'IntVar', type: 2, value: 1 },
{ _type: 'Query' },
{ _type: 'IntVar', type: 2, value: 2 },
{ _type: 'Query' },
{ _type: 'IntVar', type: 1, value: 2 },
{ _type: 'Query' },
- ], 1, function() {
- zongji.stop();
- test.done();
- });
- });
- }, 200);
+ ], 1, () => test.end()
+ );
+ }
+ });
+
+ zongji.start({
+ startAtEnd: true,
+ serverId: testDb.serverId(),
+ includeEvents: ['intvar', 'query'],
});
- },
-};
+ zongji.on('ready', () => {
+ testDb.execute([
+ 'SET SESSION binlog_format=STATEMENT',
+ `INSERT INTO ${TEST_TABLE} (col) VALUES (10)`,
+ `INSERT INTO ${TEST_TABLE} (col) VALUES (11)`,
+ `INSERT INTO ${TEST_TABLE} (id, col) VALUES (100, LAST_INSERT_ID())`,
+ // Other tests expect row-based replication, so reset here
+ 'SET SESSION binlog_format=ROW',
+ ], err => {
+ if (err) {
+ test.fail(err);
+ }
+ });
+ });
+
+ });
+
+ test.end();
+});
+
+tap.test('With many columns', test => {
+ const TEST_TABLE = '33_columns';
+ const events = [];
+
+ const zongji = new ZongJi(settings.connection);
+
+ test.tearDown(() => zongji.stop());
+ zongji.on('binlog', evt => events.push(evt));
+ zongji.start({
+ startAtEnd: true,
+ serverId: testDb.serverId(),
+ includeEvents: ['tablemap', 'writerows'],
+ });
+
+ zongji.on('ready', () => {
+ testDb.execute([
+ `DROP TABLE IF EXISTS ${TEST_TABLE}`,
+ `CREATE TABLE ${TEST_TABLE} (
+ col1 INT SIGNED NULL, col2 BIGINT SIGNED NULL,
+ col3 TINYINT SIGNED NULL, col4 SMALLINT SIGNED NULL,
+ col5 MEDIUMINT SIGNED NULL, col6 INT SIGNED NULL,
+ col7 BIGINT SIGNED NULL, col8 TINYINT SIGNED NULL,
+ col9 SMALLINT SIGNED NULL, col10 INT SIGNED NULL,
+ col11 BIGINT SIGNED NULL, col12 TINYINT SIGNED NULL,
+ col13 SMALLINT SIGNED NULL, col14 INT SIGNED NULL,
+ col15 BIGINT SIGNED NULL, col16 TINYINT SIGNED NULL,
+ col17 SMALLINT SIGNED NULL, col18 INT SIGNED NULL,
+ col19 BIGINT SIGNED NULL, col20 TINYINT SIGNED NULL,
+ col21 SMALLINT SIGNED NULL, col22 INT SIGNED NULL,
+ col23 BIGINT SIGNED NULL, col24 TINYINT SIGNED NULL,
+ col25 SMALLINT SIGNED NULL, col26 INT SIGNED NULL,
+ col27 BIGINT SIGNED NULL, col28 TINYINT SIGNED NULL,
+ col29 SMALLINT SIGNED NULL, col30 INT SIGNED NULL,
+ col31 BIGINT SIGNED NULL, col32 TINYINT SIGNED NULL,
+ col33 SMALLINT SIGNED NULL)`,
+ `INSERT INTO ${TEST_TABLE} (col1, col2, col3, col4, col5, col33) VALUES
+ (null, null, null, null, null, null),
+ (-1, -1, -1, -1, -1, -1),
+ (2147483647, 9007199254740993, 127, 32767, 8388607, 12),
+ (-2147483648, -9007199254740993, -128, -32768, -8388608, 10),
+ (-2147483645, -1, -126, -32766, -8388606, 6),
+ (-1, 9223372036854775809, -1, -1, null, -6),
+ (123456, -9223372036854775809, 96, 300, 1000, null),
+ (-123456, 9223372036854775807, -96, -300, -1000, null)`,
+ `SELECT * FROM ${TEST_TABLE}`,
+ ], (err, result) => {
+ if (err) {
+ return test.fail(err);
+ }
+
+ expectEvents(test, events, [
+ { _type: 'TableMap' },
+ { rows: result[result.length - 1], _type: 'WriteRows' }
+ ], 1, test.end);
+ });
+ });
+});
diff --git a/test/filtering.js b/test/filtering.js
index 66f08882..dc3826bf 100644
--- a/test/filtering.js
+++ b/test/filtering.js
@@ -1,149 +1,189 @@
-var settings = require('./settings/mysql');
-var connector = require('./helpers/connector');
-var querySequence = require('./helpers/querySequence');
-
-var conn = process.testZongJi || {};
-
-module.exports = {
- setUp: function(done) {
- if (!conn.db) {
- process.testZongJi = connector.call(conn, settings, done);
- } else {
- conn.incCount();
- done();
- }
- },
- tearDown: function(done) {
- if (conn) {
- conn.eventLog.splice(0, conn.eventLog.length);
- conn.errorLog.splice(0, conn.errorLog.length);
- conn.closeIfInactive(1000);
+const tap = require('tap');
+const ZongJi = require('../');
+const settings = require('./settings/mysql');
+const testDb = require('./helpers');
+
+// this test is only used for initialization
+tap.test('Initialise testing db', test => {
+ testDb.init(err => {
+ if (err) {
+ return test.fail(err);
}
- done();
- },
- unitTestFilter: function(test) {
- var origOptions = conn.zongji.options;
-
- conn.zongji.set({
- includeEvents: ['tablemap', 'writerows', 'updaterows', 'rotate'],
- excludeEvents: ['rotate'],
- includeSchema: {db1: true, db2: ['one_table'], db3: true},
- excludeSchema: {db3: true}
- });
- // Check that exclude overrides include
- test.ok(!conn.zongji._skipEvent('tablemap'));
- test.ok(conn.zongji._skipEvent('rotate'));
- test.ok(!conn.zongji._skipSchema('db1', 'any_table'));
- test.ok(!conn.zongji._skipSchema('db2', 'one_table'));
- test.ok(conn.zongji._skipSchema('db2', 'another_table'));
- test.ok(conn.zongji._skipSchema('db3', 'any_table'));
+ test.end();
+ });
+});
+
+tap.test('Unit test', test => {
+ const zongji = new ZongJi(settings.connection);
+
+ test.test('Check that exclude overrides include', test => {
+ zongji._filters({
+ includeEvents: ['tablemap', 'writerows', 'updaterows', 'rotate'],
+ excludeEvents: ['rotate'],
+ includeSchema: {db1: true, db2: ['one_table'], db3: true},
+ excludeSchema: {db3: true}
+ });
+ test.ok(!zongji._skipEvent('tablemap'));
+ test.ok(zongji._skipEvent('rotate'));
+ test.ok(!zongji._skipSchema('db1', 'any_table'));
+ test.ok(!zongji._skipSchema('db2', 'one_table'));
+ test.ok(zongji._skipSchema('db2', 'another_table'));
+ test.ok(zongji._skipSchema('db3', 'any_table'));
- conn.zongji.set({
- includeSchema: {db1: ['just_me']}
+ test.end();
});
- test.ok(!conn.zongji._skipSchema('db1', 'just_me'));
- test.ok(conn.zongji._skipSchema('db2', 'anything_else'));
- test.ok(conn.zongji._skipSchema('db1', 'not_me'));
+ test.test(test => {
+ zongji._filters({
+ includeSchema: {db1: ['just_me']}
+ });
+ test.ok(!zongji._skipSchema('db1', 'just_me'));
+ test.ok(zongji._skipSchema('db2', 'anything_else'));
+ test.ok(zongji._skipSchema('db1', 'not_me'));
- conn.zongji.set({
- excludeSchema: {db1: ['not_me']}
+ test.end();
});
- test.ok(!conn.zongji._skipSchema('db1', 'anything_else'));
- test.ok(!conn.zongji._skipSchema('db2', 'anything_else'));
- test.ok(conn.zongji._skipSchema('db1', 'not_me'));
+ test.test(test => {
+ zongji._filters({
+ excludeSchema: {db1: ['not_me']}
+ });
- conn.zongji.set({
- excludeEvents: ['rotate']
+ test.ok(!zongji._skipSchema('db1', 'anything_else'));
+ test.ok(!zongji._skipSchema('db2', 'anything_else'));
+ test.ok(zongji._skipSchema('db1', 'not_me'));
+
+ test.end();
});
- test.ok(!conn.zongji._skipEvent('tablemap'));
- test.ok(conn.zongji._skipEvent('rotate'));
+ test.test(test =>{
+ zongji._filters({
+ excludeEvents: ['rotate']
+ });
+ test.ok(!zongji._skipEvent('tablemap'));
+ test.ok(zongji._skipEvent('rotate'));
- conn.zongji.set({
- includeEvents: ['rotate']
+ test.end();
});
- test.ok(conn.zongji._skipEvent('tablemap'));
- test.ok(!conn.zongji._skipEvent('rotate'));
-
- // Restore original emitter
- delete conn.zongji.emit;
- conn.zongji.set(origOptions);
-
- test.done();
- },
- integrationTestFilter: function(test) {
- // Set includeSchema to not include anything, recieve no row events
- // Ensure that filters are applied
- var origOptions = conn.zongji.options;
- var testTable = 'filter_test';
- var includeSchema = {};
- // Uncomment the following line to manually test this test:
- // includeSchema[settings.database] = [ testTable ];
- conn.zongji.set({
- includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
- includeSchema: includeSchema
+
+ test.test(test =>{
+ test.plan(2);
+ zongji._filters({
+ includeEvents: ['rotate'],
+ });
+ test.ok(zongji._skipEvent('tablemap'));
+ test.ok(!zongji._skipEvent('rotate'));
});
- querySequence(conn.db, [
- 'DROP TABLE IF EXISTS ' + conn.escId(testTable),
- 'CREATE TABLE ' + conn.escId(testTable) + ' (col INT UNSIGNED)',
- 'INSERT INTO ' + conn.escId(testTable) + ' (col) VALUES (10)',
- 'UPDATE ' + conn.escId(testTable) + ' SET col = 15',
- 'DELETE FROM ' + conn.escId(testTable)
- ], function(error) {
- if (error) console.error(error);
+
+ test.end();
+});
+
+tap.test('Exclue all the schema', test => {
+ const zongji = new ZongJi(settings.connection);
+
+ const eventLog = [];
+ const errorLog = [];
+
+ zongji.on('binlog', event => eventLog.push(event));
+ zongji.on('error', error => errorLog.push(error));
+
+ test.tearDown(() => zongji.stop());
+
+ // Set includeSchema to not include anything, recieve no row events
+ // Ensure that filters are applied
+ const includeSchema = {};
+ zongji.start({
+ includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
+ includeSchema: includeSchema
+ });
+
+ zongji.on('ready', () => {
+ const testTable = 'filter_test';
+ testDb.execute([
+ `DROP TABLE IF EXISTS ${testTable}`,
+ `CREATE TABLE ${testTable} (col INT UNSIGNED)`,
+ `INSERT INTO ${testTable} (col) VALUES (10)`,
+ `UPDATE ${testTable} SET col = 15`,
+ `DELETE FROM ${testTable}`,
+ ], (error) => {
+ if (error) {
+ return test.fail(error);
+ }
+
// Give 1 second to see if any events are emitted, they should not be!
- setTimeout(function() {
- conn.zongji.set(origOptions);
- test.equal(conn.eventLog.length, 0);
- test.equal(conn.errorLog.length, 0);
- test.done();
+ setTimeout(() => {
+ test.equal(eventLog.length, 0);
+ test.equal(errorLog.length, 0);
+ test.end();
}, 1000);
});
- },
- changeAfterInit: function(test) {
- // Set includeSchema to skip table after the tableMap has already been
- // cached once, recieve no row events afterwards
- var origOptions = conn.zongji.options;
- var testTable = 'after_init_test';
- var includeSchema = {};
- includeSchema[settings.database] = [ testTable ];
- conn.zongji.set({
- includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
- includeSchema: includeSchema
- });
- querySequence(conn.db, [
- 'DROP TABLE IF EXISTS ' + conn.escId(testTable),
- 'CREATE TABLE ' + conn.escId(testTable) + ' (col INT UNSIGNED)',
- 'INSERT INTO ' + conn.escId(testTable) + ' (col) VALUES (10)',
- ], function(error) {
- if (error) console.error(error);
- // Give 1 second to see if any events are emitted, they should not be!
- setTimeout(function() {
- // Expect 2 events, TableMap and WriteRows from the INSERT query
- test.equal(conn.eventLog.length, 2);
- // Reset eventLog
- conn.eventLog.splice(0, conn.eventLog.length);
- // Skip all events from all tables
- conn.zongji.set({
- includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
- includeSchema: {}
- });
- querySequence(conn.db, [
- 'UPDATE ' + conn.escId(testTable) + ' SET col = 15',
- 'DELETE FROM ' + conn.escId(testTable)
- ], function(error) {
- if (error) console.error(error);
- setTimeout(function() {
- conn.zongji.set(origOptions);
- test.equal(conn.eventLog.length, 0);
- test.equal(conn.errorLog.length, 0);
- test.done();
- }, 500);
+ });
+});
+
+tap.test('Change filter when ZongJi is running', test => {
+ // Set includeSchema to skip table after the tableMap has already been
+ // cached once, recieve no row events afterwards
+ const testTable = 'after_init_test';
+ const includeSchema = {};
+ includeSchema[settings.connection.database] = [ testTable ];
+
+ const zongji = new ZongJi(settings.connection);
+ const eventLog = [];
+
+ zongji.on('binlog', event => eventLog.push(event));
+ zongji.on('error', error => test.fail(error));
+
+ zongji.start({
+ includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
+ includeSchema: includeSchema
+ });
+
+ test.tearDown(() => zongji.stop());
+
+ testDb.execute(
+ [
+ `DROP TABLE IF EXISTS ${testTable}`,
+ `CREATE TABLE ${testTable} (col INT UNSIGNED)`,
+ `INSERT INTO ${testTable} (col) VALUES (10)`,
+ ],
+ err => {
+ if (err) {
+ return test.fail(err);
+ }
+
+ setTimeout(() => {
+ test.equal(eventLog.length, 2);
+
+ test.test('update filter', test => {
+ // reset for next test
+ eventLog.splice(0, eventLog.length);
+
+ zongji._filters({
+ includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
+ includeSchema: {},
+ });
+
+ testDb.execute(
+ [
+ `UPDATE ${testTable} SET col = 15`,
+ `DELETE FROM ${testTable}`,
+ ],
+ (error) => {
+ if (error) {
+ return test.fail(error);
+ }
+
+ setTimeout(() => {
+ test.equal(eventLog.length, 0);
+ test.end();
+ }, 1000);
+ }
+ );
});
- }, 500);
- });
- }
-};
+
+ test.end();
+ }, 1000);
+ }
+ );
+});
diff --git a/test/helpers/connector.js b/test/helpers/connector.js
deleted file mode 100644
index 15c54989..00000000
--- a/test/helpers/connector.js
+++ /dev/null
@@ -1,66 +0,0 @@
-var ZongJi = require('./../../');
-var mysql = require('mysql');
-var querySequence = require('./querySequence');
-
-module.exports = function(settings, callback) {
- var self = this;
- var db = self.db = mysql.createConnection(settings.connection);
- var escId = self.escId = db.escapeId;
- var eventLog = self.eventLog = [];
- var errorLog = self.errorLog = [];
-
- self.dbName = settings.database;
- self.testCount = 0;
-
- // Perform initialization queries sequentially
- querySequence(db, [
- 'SET GLOBAL sql_mode = \'' + settings.sessionSqlMode + '\'',
- 'DROP DATABASE IF EXISTS ' + escId(settings.database),
- 'CREATE DATABASE ' + escId(settings.database),
- 'USE ' + escId(settings.database),
- 'RESET MASTER',
- 'SELECT VERSION() AS version'
- ], function(error, results) {
- if (error) console.error(error);
-
- self.mysqlVersion = results[results.length - 1][0].version
- .split('-')[0]
- .split('.')
- .map(function(part) {
- return parseInt(part, 10);
- });
-
- var zongji = self.zongji = new ZongJi(settings.connection);
-
- zongji.on('binlog', function(event) {
- eventLog.push(event);
- });
-
- zongji.on('error', function(error) {
- errorLog.push(error);
- });
-
- zongji.start({
- includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows']
- });
-
- callback();
- });
-
- // Extra methods on connector object
- self.incCount = function() {
- self.testCount++;
- };
-
- self.closeIfInactive = function(interval) {
- var startCount = self.testCount;
- setTimeout(function() {
- if (startCount === self.testCount) {
- self.zongji.stop();
- self.db.destroy();
- }
- }, interval);
- };
-
- return self;
-};
diff --git a/test/helpers/expectEvents.js b/test/helpers/expectEvents.js
index 0ffb101d..a65069a6 100644
--- a/test/helpers/expectEvents.js
+++ b/test/helpers/expectEvents.js
@@ -1,4 +1,4 @@
-var MAX_WAIT = 3000;
+const MAX_WAIT = 3000;
// Check an array of events against an array of expectations
// @param {object} test - Pass-thru from nodeunit test case
@@ -20,15 +20,15 @@ function expectEvents(test, events, expected, multiplier, callback, waitIndex) {
} else {
test.strictEqual(events.length, expected.length * multiplier);
events.forEach(function(event, index) {
- var exp = expected[index % expected.length];
- for (var i in exp) {
- if (exp.hasOwnProperty(i)) {
+ const exp = expected[index % expected.length];
+ for (const i in exp) {
+ if (Object.prototype.hasOwnProperty.call(exp, i)) {
if (i === '_type') {
- test.strictEqual(exp[i], event.getTypeName());
+ test.strictEqual(event.getTypeName(), exp[i]);
} else if (String(i).substr(0, 1) === '_') {
exp[i](test, event);
} else {
- test.deepEqual(exp[i], event[i]);
+ test.same(event[i], exp[i]);
}
}
}
diff --git a/test/helpers/index.js b/test/helpers/index.js
new file mode 100644
index 00000000..6c5a77ae
--- /dev/null
+++ b/test/helpers/index.js
@@ -0,0 +1,94 @@
+const mysql = require('mysql');
+
+const settings = require('../settings/mysql');
+const querySequence = require('./querySequence');
+
+const SCHEMA_NAME = settings.connection.database;
+
+exports.SCHEMA_NAME = SCHEMA_NAME;
+
+exports.init = function(done) {
+ const connObj = {...settings.connection};
+ // database doesn't exist at this time
+ delete connObj.database;
+ const conn = mysql.createConnection(connObj);
+
+ querySequence(
+ conn,
+ [
+ 'SET GLOBAL sql_mode = \'' + settings.sessionSqlMode + '\'',
+ `DROP DATABASE IF EXISTS ${SCHEMA_NAME}`,
+ `CREATE DATABASE ${SCHEMA_NAME}`,
+ `USE ${SCHEMA_NAME}`,
+ 'RESET MASTER',
+ // 'SELECT VERSION() AS version'
+ ],
+ error => {
+ conn.destroy();
+ done(error);
+ }
+ );
+};
+
+exports.execute = function(queries, done) {
+ const conn = mysql.createConnection(settings.connection);
+ querySequence(
+ conn,
+ queries,
+ (error, result) => {
+ conn.destroy();
+ done(error, result);
+ }
+ );
+};
+
+const checkVersion = function(expected, actual) {
+ const parts = expected.split('.').map(part => parseInt(part, 10));
+ for (let i = 0; i < parts.length; i++) {
+ if (actual[i] == parts[i]) {
+ continue;
+ }
+ return actual[i] > parts[i];
+ }
+ return true;
+};
+
+exports.requireVersion = function(expected, done) {
+ const connObj = {...settings.connection};
+ // database doesn't exist at this time
+ delete connObj.database;
+ const conn = mysql.createConnection(connObj);
+ querySequence(conn, ['SELECT VERSION() AS version'], (err, results) => {
+ conn.destroy();
+
+ if (err) {
+ throw err;
+ }
+
+ let ver = results[results.length - 1][0]
+ .version.split('-')[0]
+ .split('.')
+ .map(part => parseInt(part, 10));
+
+ if (checkVersion(expected, ver)) {
+ done();
+ }
+ });
+};
+
+let id = 100;
+exports.serverId = function() {
+ id ++;
+ return id;
+};
+
+exports.strRepeat = function (pattern, count) {
+ if (count < 1) return '';
+ let result = '';
+ let pos = 0;
+ while (pos < count) {
+ result += pattern.replace(/##/g, pos);
+ pos++;
+ }
+ return result;
+};
diff --git a/test/helpers/querySequence.js b/test/helpers/querySequence.js
index 75312c06..9a748258 100644
--- a/test/helpers/querySequence.js
+++ b/test/helpers/querySequence.js
@@ -9,8 +9,8 @@ module.exports = function(connection, debug, queries, callback) {
queries = debug;
debug = false;
}
- var results = [];
- var sequence = queries.map(function(queryStr, index) {
+ const results = [];
+ const sequence = queries.map(function(queryStr, index) {
return function() {
debug && console.log('Query Sequence', index, queryStr);
connection.query(queryStr, function(err, rows) {
diff --git a/test/helpers/strRepeat.js b/test/helpers/strRepeat.js
deleted file mode 100644
index 3a070493..00000000
--- a/test/helpers/strRepeat.js
+++ /dev/null
@@ -1,10 +0,0 @@
-module.exports = function (pattern, count) {
- if (count < 1) return '';
- var result = '';
- var pos = 0;
- while (pos < count) {
- result += pattern.replace(/##/g, pos);
- pos++;
- }
- return result;
-};
diff --git a/test/rowimage.js b/test/rowimage.js
new file mode 100644
index 00000000..f3256457
--- /dev/null
+++ b/test/rowimage.js
@@ -0,0 +1,162 @@
+const tap = require('tap');
+
+const ZongJi = require('../');
+const testDb = require('./helpers');
+const expectEvents = require('./helpers/expectEvents');
+const settings = require('./settings/mysql');
+
+tap.test('Initialise testing db', test => {
+ testDb.init(err => {
+ if (err) {
+ return test.threw(err);
+ }
+ test.end();
+ });
+});
+
+testDb.requireVersion('5.6.2', () => {
+
+ tap.test('Update with binlog_row_image=minmal', test => {
+ const TEST_TABLE = 'row_image_minimal_test';
+
+ test.test(`prepare table ${TEST_TABLE}`, test => {
+ testDb.execute([
+ 'SET GLOBAL binlog_row_image=minimal',
+ `DROP TABLE IF EXISTS ${TEST_TABLE}`,
+ `CREATE TABLE ${TEST_TABLE} (
+ id int primary key auto_increment,
+ name varchar(20),
+ age tinyint,
+ height mediumint
+ )`,
+ `INSERT INTO ${TEST_TABLE} (name, age) VALUES ('Tom', 2)`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
+
+ test.end();
+ });
+ });
+
+ test.test('update a record', test => {
+ const events = [];
+ const zongji = new ZongJi(settings.connection);
+ test.tearDown(() => zongji.stop());
+
+ zongji.on('ready', () => {
+ testDb.execute([
+ `UPDATE ${TEST_TABLE} SET age=age+1 WHERE id=1`,
+ ], err => {
+ if (err) {
+ test.fail(err);
+ }
+ });
+ });
+
+ zongji.on('binlog', evt => {
+ events.push(evt);
+
+ if (events.length == 2) {
+ expectEvents(test, events,
+ [
+ {
+ _type: 'TableMap',
+ tableName: TEST_TABLE,
+ schemaName: testDb.SCHEMA_NAME,
+ },
+ {
+ _type: 'UpdateRows',
+ rows: [
+ {
+ before: { id: 1, age: null, name: null, height: null },
+ after: { id: null, age: 3, name: null, height: null },
+ },
+ ],
+ }
+ ], 1, () => test.end()
+ );
+ }
+ });
+
+ zongji.start({
+ startAtEnd: true,
+ serverId: testDb.serverId(),
+ includeEvents: ['tablemap', 'updaterows'],
+ });
+ });
+
+ test.end();
+ });
+
+ tap.test('Update with binlog_row_image=noblob', test => {
+ const TEST_TABLE = 'row_image_noblob_test';
+
+ test.test(`prepare table ${TEST_TABLE}`, test => {
+ testDb.execute([
+ 'SET GLOBAL binlog_row_image=noblob',
+ `DROP TABLE IF EXISTS ${TEST_TABLE}`,
+ `CREATE TABLE ${TEST_TABLE} (
+ id int primary key auto_increment,
+ summary text
+ )`,
+ `INSERT INTO ${TEST_TABLE} (summary) VALUES ('Hello world')`,
+ ], err => {
+ if (err) {
+ return test.fail(err);
+ }
+
+ test.end();
+ });
+ });
+
+ test.test('update a record', test => {
+ const events = [];
+ const zongji = new ZongJi(settings.connection);
+ test.tearDown(() => zongji.stop());
+
+ zongji.on('ready', () => {
+ testDb.execute([
+ `UPDATE ${TEST_TABLE} SET summary='hello again' WHERE id=1`,
+ ], err => {
+ if (err) {
+ test.fail(err);
+ }
+ });
+ });
+
+ zongji.on('binlog', evt => {
+ events.push(evt);
+
+ if (events.length == 2) {
+ expectEvents(test, events,
+ [
+ {
+ _type: 'TableMap',
+ tableName: TEST_TABLE,
+ schemaName: testDb.SCHEMA_NAME,
+ },
+ {
+ _type: 'UpdateRows',
+ rows: [
+ {
+ before: { id: 1, summary: null },
+ after: { id: 1, summary: 'hello again' },
+ },
+ ],
+ }
+ ], 1, () => test.end()
+ );
+ }
+ });
+
+ zongji.start({
+ startAtEnd: true,
+ serverId: testDb.serverId(),
+ includeEvents: ['tablemap', 'updaterows'],
+ });
+ });
+
+ test.end();
+ });
+});
diff --git a/test/settings/mysql.js b/test/settings/mysql.js
index a4705931..e3ef27a0 100644
--- a/test/settings/mysql.js
+++ b/test/settings/mysql.js
@@ -8,8 +8,8 @@ module.exports = {
charset : 'utf8mb4_unicode_ci',
port : process.env.TEST_MYSQL_PORT,
dateStrings : process.env.TEST_DATE_STRINGS === 'true',
+ database: 'zongji_test',
// debug: true
},
- database: 'zongji_test',
- sessionSqlMode: process.env.TEST_SESSION_SQL_MODE || ''
+ sessionSqlMode: process.env.TEST_SESSION_SQL_MODE || '',
};
diff --git a/test/types.js b/test/types.js
index 38cc5543..7f808187 100644
--- a/test/types.js
+++ b/test/types.js
@@ -1,125 +1,100 @@
-var settings = require('./settings/mysql');
-var connector = require('./helpers/connector');
-var querySequence = require('./helpers/querySequence');
-var expectEvents = require('./helpers/expectEvents');
-var strRepeat = require('./helpers/strRepeat');
-
-var conn = process.testZongJi || {};
-
-module.exports = {
- setUp: function(done) {
- if (!conn.db) {
- process.testZongJi = connector.call(conn, settings, done);
- } else {
- conn.incCount();
- done();
- }
- },
- tearDown: function(done) {
- if (conn) {
- conn.eventLog.splice(0, conn.eventLog.length);
- conn.errorLog.splice(0, conn.errorLog.length);
- conn.closeIfInactive(1000);
- }
- done();
- }
-};
+const tap = require('tap');
+const ZongJi = require('../');
+const expectEvents = require('./helpers/expectEvents');
+const testDb = require('./helpers');
+const settings = require('./settings/mysql');
+const strRepeat = testDb.strRepeat;
+
// @param {string} name - unique identifier of this test [a-zA-Z0-9]
// @param {[string]} fields - MySQL field description e.g. `BIGINT NULL`
// @param {[[any]]} testRows - 2D array of rows and fields to insert and test
// @param {func} customTest - optional, instead of exact row check
-// @param {string} minVersion - optional, e.g. '5.6.4'
-var defineTypeTest = function(name, fields, testRows, customTest, minVersion) {
- // Allow skipping customTest argument and passing minVersion in its place
- if (typeof customTest === 'string') {
- minVersion = customTest;
- customTest = undefined;
- }
-
- module.exports[name] = function(test) {
- var testTable = 'type_' + name;
- var fieldText = fields.map(function(field, index) {
- return 'col' + index + ' ' + field;
- }).join(', ');
- var insertColumns = fields.map(function(field, index) {
- return 'col' + index;
- }).join(', ');
- var testQueries = [
- 'DROP TABLE IF EXISTS ' + conn.escId(testTable),
- 'CREATE TABLE ' + conn.escId(testTable) + ' (' + fieldText + ')',
- 'SET @@session.time_zone = "+00:00"']
- .concat(testRows.map(function(row) {
- return 'INSERT INTO ' + conn.escId(testTable) +
- ' (' + insertColumns + ') VALUES (' +
- row.map(function(field) {
- return field === null ? 'null' : field;
- }).join(', ') + ')';
- }))
- .concat([
- 'SET @@session.time_zone = "SYSTEM"',
- 'SELECT * FROM ' + conn.escId(testTable)
- ]);
-
- if (!minVersion || checkVersion(minVersion, conn.mysqlVersion)) {
- querySequence(conn.db, testQueries, function(error, results) {
- if (error) console.error(error);
- var selectResult = results[results.length - 1];
- var expectedWrite = {
- _type: 'WriteRows',
- _checkTableMap: function(test, event) {
- var tableDetails = event.tableMap[event.tableId];
- test.strictEqual(tableDetails.parentSchema, settings.database);
- test.strictEqual(tableDetails.tableName, testTable);
- }
- };
-
- expectEvents(test, conn.eventLog, [
- {
- _type: 'TableMap',
- tableName: testTable,
- schemaName: settings.database
- },
- expectedWrite
- ], testRows.length, function() {
- test.equal(conn.errorLog.length, 0);
- conn.errorLog.length &&
- console.log('Type Test Error: ', name, conn.errorLog);
- if (conn.errorLog.length) {
- throw conn.errorLog[0];
+function defineTypeTest(name, fields, testRows, customTest) {
+ const TEST_TABLE = 'type_' + name;
+ const fieldText = fields.map((field, index) => `col${index} ${field}`).join(',');
+ const insertColumns = fields.map((field, index) => 'col' + index).join(',');
+ const testQueries = [
+ `CREATE TABLE ${TEST_TABLE} (${fieldText})`,
+ 'SET @@session.time_zone = "+00:00"']
+ .concat(
+ testRows.map(row => `INSERT INTO ${TEST_TABLE}
+ (${insertColumns}) VALUES
+ (${row.map(field => field === null ? 'null' : field).join(',')})`
+ )
+ )
+ .concat([
+ 'SET @@session.time_zone = "SYSTEM"',
+ `SELECT * FROM ${TEST_TABLE}`,
+ ]);
+
+ tap.test('Initialise testing db', test => {
+ testDb.init(err => {
+ if (err) {
+ return test.fail(err);
+ }
+
+ test.end();
+ });
+ });
+
+ tap.test(name, test => {
+ const eventLog = [];
+ const errorLog = [];
+
+ const zongji = new ZongJi(settings.connection);
+ test.tearDown(() => zongji.stop());
+
+ zongji.start({
+ includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'],
+ serverId: testDb.serverId(),
+ });
+ zongji.on('binlog', event => eventLog.push(event));
+ zongji.on('error', error => errorLog.push(error));
+ zongji.on('ready', () => {
+ testDb.execute(testQueries, (error, results) => {
+ if (error) {
+ return test.fail(error);
}
- var binlogRows = conn.eventLog.reduce(function(prev, curr) {
- if (curr.getTypeName() === 'WriteRows') {
- prev = prev.concat(curr.rows);
+ const selectResult = results[results.length - 1];
+ const expectedWrite = {
+ _type: 'WriteRows',
+ _checkTableMap: (test, event) => {
+ const tableDetails = event.tableMap[event.tableId];
+ test.same(tableDetails.parentSchema, testDb.SCHEMA_NAME);
+ test.same(tableDetails.tableName, TEST_TABLE);
}
- return prev;
- }, []);
+ };
- if (customTest) {
- customTest.bind(selectResult)(test, { rows: binlogRows });
- } else {
- test.deepEqual(selectResult, binlogRows);
- }
+ expectEvents(test, eventLog, [
+ {
+ _type: 'TableMap',
+ tableName: TEST_TABLE,
+ schemaName: testDb.SCHEMA_NAME,
+ },
+ expectedWrite
+ ], testRows.length, () => {
+ test.equal(errorLog.length, 0);
+
+ const binlogRows = eventLog.reduce((prev, curr) => {
+ if (curr.getTypeName() === 'WriteRows') {
+ prev = prev.concat(curr.rows);
+ }
+ return prev;
+ }, []);
+
+ if (customTest) {
+ customTest.bind(selectResult)(test, { rows: binlogRows });
+ } else {
+ test.deepEqual(selectResult, binlogRows);
+ }
- test.done();
+ test.end();
+ });
});
});
- } else {
- // Skip running test when version doesn't meet minVersion
- test.done();
- }
- };
-};
-
-var checkVersion = function(check, actual) {
- var parts = check.split('.').map(function(part) {
- return parseInt(part, 10);
- });
- for (var i = 0; i < parts.length; i++) {
- if (actual[i] > parts[i]) return true;
- else if (actual[i] < parts[i]) return false;
- }
-};
+ });
+}
// Begin test case definitions
@@ -201,17 +176,17 @@ defineTypeTest('int_unsigned', [
defineTypeTest('double', [
'DOUBLE NULL'
], [
- [1.0], [-1.0], [123.456], [-13.47], [0.00005], [-0.00005],
+ [0], [1.0], [-1.0], [123.456], [-13.47], [0.00005], [-0.00005],
[8589934592.123], [-8589934592.123], [null]
]);
defineTypeTest('float', [
'FLOAT NULL'
], [
- [1.0], [-1.0], [123.456], [-13.47], [3999.12]
+ [0], [1.0], [-1.0], [123.456], [-13.47], [3999.12]
], function(test, event) {
// Ensure sum of differences is very low
- var diff = event.rows.reduce(function(prev, cur, index) {
+ const diff = event.rows.reduce(function(prev, cur, index) {
return prev + Math.abs(cur.col0 - this[index].col0);
}.bind(this), 0);
test.ok(diff < 0.001);
@@ -261,20 +236,6 @@ defineTypeTest('time_no_fraction', [
["'-01:27:28'"],
]);
-defineTypeTest('time_fraction', [
- 'TIME(0) NULL',
- 'TIME(1) NULL',
- 'TIME(3) NULL',
- 'TIME(6) NULL'
-], [
- ["'-00:00:01'", "'-00:00:01.1'", "'-00:00:01.002'", "'-00:00:01.123456'"],
- ["'00:00:00'", "'00:00:00.2'", "'00:00:00.123'", "'-00:00:00.000001'"],
- ["'00:07:00'", "'00:07:00.3'", "'00:07:00.654'", "'00:07:00.010203'"],
- ["'20:00:00'", "'20:00:00.4'", "'20:00:00.090'", "'20:00:00.987654'"],
- ["'19:00:00'", "'19:00:00.5'", "'19:00:00.999'", "'19:00:00.000001'"],
- ["'04:00:00'", "'04:00:00.0'", "'04:00:00.01'", "'04:00:00.1'"],
-], '5.6.4');
-
defineTypeTest('datetime_no_fraction', [
'DATETIME NULL'
], [
@@ -283,30 +244,6 @@ defineTypeTest('datetime_no_fraction', [
["'2014-12-27 01:07:08'"]
]);
-defineTypeTest('datetime_fraction', [
- 'DATETIME(0) NULL',
- 'DATETIME(1) NULL',
- 'DATETIME(4) NULL',
- 'DATETIME(6) NULL'
-], [
- ["'1000-01-01 00:00:00'", "'1000-01-01 00:00:00.5'",
- "'1000-01-01 00:00:00.9999'", "'1000-01-01 00:00:00.123456'"],
- ["'9999-12-31 23:59:59'", "'9999-12-31 23:59:59.9'",
- "'9999-12-31 23:59:59.6543'", "'9999-12-31 23:59:59.000001'"],
- ["'9999-12-31 23:59:59'", "'9999-12-31 23:59:59.1'",
- "'9999-12-31 23:59:59.1234'", "'9999-12-31 23:59:59.4326'" ],
- ["'2014-12-27 01:07:08'", "'2014-12-27 01:07:08.0'",
- "'2014-12-27 01:07:08.0001'", "'2014-12-27 01:07:08.05'" ]
-], '5.6.4');
-
-defineTypeTest('timestamp_fractional', [
- 'TIMESTAMP(3) NULL',
-], [
- ["'1970-01-01 00:00:01.123'"],
- ["'2038-01-18 03:14:07.900'"],
- ["'2014-12-27 01:07:08.001'"],
-], '5.6.4');
-
defineTypeTest('temporal_other', [
'DATE NULL',
'TIMESTAMP NULL',
@@ -339,163 +276,209 @@ defineTypeTest('text', [
[null, null, null, null]
]);
-defineTypeTest('utf8mb4', [
- 'VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'
-], [
- ["'á'"], // 3 byte character
- ["'𠜎'"], // 4 byte character
-], '5.5.3');
+// ======= below require different version of MySQL =======
-defineTypeTest('datetime_then_decimal', [
- 'DATETIME(3) NULL',
- 'DECIMAL(30, 10) NULL'
-], [
- ["'1000-01-01 00:00:00.123'", 10.10],
- ["'9999-12-31 23:59:59.001'", -123.45],
- ["'2014-12-27 01:07:08.053'", 12345.123]
-], '5.6.4');
+testDb.requireVersion('5.5.3', () => {
+ defineTypeTest('utf8mb4', [
+ 'VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'
+ ], [
+ ["'á'"], // 3 byte character
+ ["'𠜎'"], // 4 byte character
+ ]);
+});
-defineTypeTest('json', [
- 'JSON NULL'
-], [
- // Small Object
- ['\'{"key1": "value1", "key2": "value2", "key3": 34}\''],
- // Small Object with nested object
- ['\'{"key1": { "key2": "value2", "key3": 34 } }\''],
- // Small Object with double nested object
- ['\'{"key1": { "key2": { "key2": "value2", "key3": 34 }, "key3": 34 } }\''],
- // Small Object with unicode character in key and value
- ['\'{ "key2": "válue2", "keybá3": 34 }\''],
- // Large Object
- ['\'{' + strRepeat('"key##": "value##", ', 2839) + '"keyLast": 34}\''],
- // Large Object with nested small objects
- ['\'{' + strRepeat('"key##": {"subkey": "value##"}, ', 2000) + '"keyLast": 34}\''],
- // Large Object with nested small arrays
- ['\'{' + strRepeat('"key##": ["a", ##], ', 3000) + '"keyLast": 34}\''],
- // Small array
- ['\'["a", "b", 1]\''],
- // Small array with nested array
- ['\'["a", [2, "b"], 1]\''],
- // Small array with double nested array
- ['\'["a", [2, ["b", 4, 54]], 1]\''],
- // Large Array
- ['\'[' + strRepeat('"value##", ', 6000) + '34]\''],
- // Large Array with nested small objects
- ['\'[' + strRepeat('{"key##": "value##"}, ', 6000) + '34]\''],
- // Large Array with nested small arrays
- ['\'[' + strRepeat('[##, "value##"], ', 6000) + '34]\''],
- // Strings of various lengths
- ['\'"hello"\''],
- ['\'{"twobytelen": "' + strRepeat('a', 256) + '"}\''],
- ['\'{"twobytelen": "' + strRepeat('a', 257) + '"}\''],
- ['\'{"twobytelen": "' + strRepeat('a', 258) + '"}\''],
- ['\'{"twobytelen": "' + strRepeat('a', 7383) + '"}\''],
- ['\'{"twobytelen": "' + strRepeat('a', 16383) + '"}\''],
- ['\'{"threebytelen": "' + strRepeat('a', 16388) + '"}\''],
- // Integers
- ['\'{"key1": -10, "keyb": 34}\''],
- ['\'10\''],
- ['\'2147483647\''], // Int32
- ['\'-2147483647\''], // Int32
- ['\'2147483648\''], // Int64
- ['\'4294967295\''], // Int64
- ['\'-4294967295\''], // Int64
- ['\'9007199254740992\''], // UInt64
- ['\'-9007199254740992\''], // Int64
- ['\'3e2\''],
- ['\'-3e-2\''],
- // Doubles
- ['\'10.123\''],
- ['\'{"doubleval": "-123.38439", "another": 1283192.0004}\''],
- // Literals
- ['\'{"literaltest1": null, "literal2": true, "literal3": false}\''],
- ['\'{"literaltest1": null, "stringafter": "heyos", "number": 35}\''],
- ['\'null\''],
- ['\'true\''],
- ['\'false\''],
- // Opaque custom data
- ['JSON_OBJECT(\'key\', BINARY \'hi\')'],
- ['JSON_OBJECT(\'key\', MAKEDATE(2014,361))'],
- ['JSON_OBJECT(\'key\', DATE(\'100-01-01\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'1000-01-01\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'1000-01-02\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'1000-01-03\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'1000-02-01\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'1000-12-31\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'2001-01-01\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'2002-01-01\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'2003-01-01\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'2004-01-01\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'9999-01-01\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'9999-12-31\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'2002-02-02\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'2002-03-03\'))'],
- ['JSON_OBJECT(\'key\', DATE(\'2002-12-12\'))'],
- ['JSON_OBJECT(\'key\', MAKETIME(-838,59,59))'],
- ['JSON_OBJECT(\'key\', MAKETIME(838,59,59))'],
- ['JSON_OBJECT(\'zero\', MAKETIME(0,0,0))'],
- ['JSON_OBJECT(\'onehour\', MAKETIME(1,0,0))'],
- ['JSON_OBJECT(\'oneminu\', MAKETIME(0,1,0))'],
- ['JSON_OBJECT(\'oneseco\', MAKETIME(0,0,1))'],
- ['JSON_OBJECT(\'hurnsec\', MAKETIME(1,0,1))'],
- ['JSON_OBJECT(\'minnsec\', MAKETIME(0,1,1))'],
- ['JSON_OBJECT(\'2minsec\', MAKETIME(0,2,2))'],
- ['JSON_OBJECT(\'2min15sec\', MAKETIME(0,2,15))'],
- ['JSON_OBJECT(\'2min16sec\', MAKETIME(0,2,16))'],
- ['JSON_OBJECT(\'2min32sec\', MAKETIME(0,2,32))'],
- ['JSON_OBJECT(\'2min59sec\', MAKETIME(0,2,59))'],
- ['JSON_OBJECT(\'key\', MAKETIME(0,59,0))'],
- ['JSON_OBJECT(\'key\', MAKETIME(0,0,59))'],
- ['JSON_OBJECT(\'key\', MAKETIME(20,15,10))'],
- ['JSON_OBJECT(\'key\', MAKETIME(21,15,10))'],
- ['JSON_OBJECT(\'key\', MAKETIME(22,15,10))'],
- ['JSON_OBJECT(\'oneseco\', MAKETIME(0,0,1.123))'],
- ['JSON_OBJECT(\'oneseco\', MAKETIME(0,0,1.000123))'],
- ['JSON_OBJECT(\'key\', MAKETIME(-20,00,00))'],
- ['JSON_OBJECT(\'-59min\', TIME(\'-00:00:00.003\'))'],
- ['JSON_OBJECT(\'-59min\', TIME(\'00:00:00.003\'))'],
- ['JSON_OBJECT(\'-59min\', TIME(\'-00:59:59\'))'],
- ['JSON_OBJECT(\'-59min\', TIME(\'-00:59:59.0003\'))'],
- ['JSON_OBJECT(\'-1hr\', MAKETIME(-1,00,00))'],
- ['JSON_OBJECT(\'-2hr\', MAKETIME(-2,00,00))'],
- ['JSON_OBJECT(\'-1hr1sec\', MAKETIME(-1,00,1))'],
- ['JSON_OBJECT(\'-1hr1sec\', MAKETIME(-1,00,0.1))'],
- ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-27\'))'],
- ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-27 01:07:08\'))'],
- ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-27 01:07:08.123\'))'],
- ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-27 01:07:08.000456\'))'],
- ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-28\'))'],
- ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-29\'))'],
- ['JSON_OBJECT(\'key\', TIMESTAMP(\'2003-12-31 12:00:00\'))'],
- ['JSON_OBJECT(\'key\', TIMESTAMP(\'2003-12-31 12:00:00.123\'))'],
- ['JSON_OBJECT(\'key\', UNIX_TIMESTAMP(\'2015-11-13 10:20:19.012\'))'],
-], function(test, event) {
- // JSON from MySQL client has different whitespace than JSON.stringify
- // Therefore, parse and perform deep equality
- event.rows.forEach(function(row, index) {
- // test.deepEqual does not work when comparison objects exceed 65536 bytes
- // Perform alternative assertions for these large cases
- var expected = JSON.parse(this[index].col0);
- var actual = JSON.parse(row.col0);
- if (this[index].col0.length > 65536) {
- // Large cases are either array or object
- if (expected instanceof Array) {
- test.strictEqual(expected.length, actual.length);
- for (var i = 0; i < expected.length; i++) {
- test.deepEqual(expected[i], actual[i]);
+testDb.requireVersion('5.6.4', () => {
+ defineTypeTest('time_fraction', [
+ 'TIME(0) NULL',
+ 'TIME(1) NULL',
+ 'TIME(3) NULL',
+ 'TIME(6) NULL'
+ ], [
+ ["'-00:00:01'", "'-00:00:01.1'", "'-00:00:01.002'", "'-00:00:01.123456'"],
+ ["'00:00:00'", "'00:00:00.2'", "'00:00:00.123'", "'-00:00:00.000001'"],
+ ["'00:07:00'", "'00:07:00.3'", "'00:07:00.654'", "'00:07:00.010203'"],
+ ["'20:00:00'", "'20:00:00.4'", "'20:00:00.090'", "'20:00:00.987654'"],
+ ["'19:00:00'", "'19:00:00.5'", "'19:00:00.999'", "'19:00:00.000001'"],
+ ["'04:00:00'", "'04:00:00.0'", "'04:00:00.01'", "'04:00:00.1'"],
+ ]);
+
+ defineTypeTest('datetime_fraction', [
+ 'DATETIME(0) NULL',
+ 'DATETIME(1) NULL',
+ 'DATETIME(4) NULL',
+ 'DATETIME(6) NULL'
+ ], [
+ ["'1000-01-01 00:00:00'", "'1000-01-01 00:00:00.5'",
+ "'1000-01-01 00:00:00.9999'", "'1000-01-01 00:00:00.123456'"],
+ ["'9999-12-31 23:59:59'", "'9999-12-31 23:59:59.9'",
+ "'9999-12-31 23:59:59.6543'", "'9999-12-31 23:59:59.000001'"],
+ ["'9999-12-31 23:59:59'", "'9999-12-31 23:59:59.1'",
+ "'9999-12-31 23:59:59.1234'", "'9999-12-31 23:59:59.4326'" ],
+ ["'2014-12-27 01:07:08'", "'2014-12-27 01:07:08.0'",
+ "'2014-12-27 01:07:08.0001'", "'2014-12-27 01:07:08.05'" ]
+ ]);
+
+ defineTypeTest('timestamp_fractional', [
+ 'TIMESTAMP(3) NULL',
+ ], [
+ ["'1970-01-01 00:00:01.123'"],
+ ["'2038-01-18 03:14:07.900'"],
+ ["'2014-12-27 01:07:08.001'"],
+ ]);
+
+ defineTypeTest('datetime_then_decimal', [
+ 'DATETIME(3) NULL',
+ 'DECIMAL(30, 10) NULL'
+ ], [
+ ["'1000-01-01 00:00:00.123'", 10.10],
+ ["'9999-12-31 23:59:59.001'", -123.45],
+ ["'2014-12-27 01:07:08.053'", 12345.123]
+ ]);
+});
+
+testDb.requireVersion('5.7.8', () => {
+ defineTypeTest('json', [
+ 'JSON NULL'
+ ], [
+ // Small Object
+ ['\'{"key1": "value1", "key2": "value2", "key3": 34}\''],
+ // Small Object with nested object
+ ['\'{"key1": { "key2": "value2", "key3": 34 } }\''],
+ // Small Object with double nested object
+ ['\'{"key1": { "key2": { "key2": "value2", "key3": 34 }, "key3": 34 } }\''],
+ // Small Object with unicode character in key and value
+ ['\'{ "key2": "válue2", "keybá3": 34 }\''],
+ // Large Object
+ ['\'{' + strRepeat('"key##": "value##", ', 2839) + '"keyLast": 34}\''],
+ // Large Object with nested small objects
+ ['\'{' + strRepeat('"key##": {"subkey": "value##"}, ', 2000) + '"keyLast": 34}\''],
+ // Large Object with nested small arrays
+ ['\'{' + strRepeat('"key##": ["a", ##], ', 3000) + '"keyLast": 34}\''],
+ // Small array
+ ['\'["a", "b", 1]\''],
+ // Small array with nested array
+ ['\'["a", [2, "b"], 1]\''],
+ // Small array with double nested array
+ ['\'["a", [2, ["b", 4, 54]], 1]\''],
+ // Large Array
+ ['\'[' + strRepeat('"value##", ', 6000) + '34]\''],
+ // Large Array with nested small objects
+ ['\'[' + strRepeat('{"key##": "value##"}, ', 6000) + '34]\''],
+ // Large Array with nested small arrays
+ ['\'[' + strRepeat('[##, "value##"], ', 6000) + '34]\''],
+ // Strings of various lengths
+ ['\'"hello"\''],
+ ['\'{"twobytelen": "' + strRepeat('a', 256) + '"}\''],
+ ['\'{"twobytelen": "' + strRepeat('a', 257) + '"}\''],
+ ['\'{"twobytelen": "' + strRepeat('a', 258) + '"}\''],
+ ['\'{"twobytelen": "' + strRepeat('a', 7383) + '"}\''],
+ ['\'{"twobytelen": "' + strRepeat('a', 16383) + '"}\''],
+ ['\'{"threebytelen": "' + strRepeat('a', 16388) + '"}\''],
+ // Integers
+ ['\'{"key1": -10, "keyb": 34}\''],
+ ['\'10\''],
+ ['\'2147483647\''], // Int32
+ ['\'-2147483647\''], // Int32
+ ['\'2147483648\''], // Int64
+ ['\'4294967295\''], // Int64
+ ['\'-4294967295\''], // Int64
+ ['\'9007199254740992\''], // UInt64
+ ['\'-9007199254740992\''], // Int64
+ ['\'3e2\''],
+ ['\'-3e-2\''],
+ // Doubles
+ ['\'10.123\''],
+ ['\'{"doubleval": "-123.38439", "another": 1283192.0004}\''],
+ // Literals
+ ['\'{"literaltest1": null, "literal2": true, "literal3": false}\''],
+ ['\'{"literaltest1": null, "stringafter": "heyos", "number": 35}\''],
+ ['\'null\''],
+ ['\'true\''],
+ ['\'false\''],
+ // Opaque custom data
+ ['JSON_OBJECT(\'key\', BINARY \'hi\')'],
+ ['JSON_OBJECT(\'key\', MAKEDATE(2014,361))'],
+ ['JSON_OBJECT(\'key\', DATE(\'100-01-01\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'1000-01-01\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'1000-01-02\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'1000-01-03\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'1000-02-01\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'1000-12-31\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'2001-01-01\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'2002-01-01\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'2003-01-01\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'2004-01-01\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'9999-01-01\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'9999-12-31\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'2002-02-02\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'2002-03-03\'))'],
+ ['JSON_OBJECT(\'key\', DATE(\'2002-12-12\'))'],
+ ['JSON_OBJECT(\'key\', MAKETIME(-838,59,59))'],
+ ['JSON_OBJECT(\'key\', MAKETIME(838,59,59))'],
+ ['JSON_OBJECT(\'zero\', MAKETIME(0,0,0))'],
+ ['JSON_OBJECT(\'onehour\', MAKETIME(1,0,0))'],
+ ['JSON_OBJECT(\'oneminu\', MAKETIME(0,1,0))'],
+ ['JSON_OBJECT(\'oneseco\', MAKETIME(0,0,1))'],
+ ['JSON_OBJECT(\'hurnsec\', MAKETIME(1,0,1))'],
+ ['JSON_OBJECT(\'minnsec\', MAKETIME(0,1,1))'],
+ ['JSON_OBJECT(\'2minsec\', MAKETIME(0,2,2))'],
+ ['JSON_OBJECT(\'2min15sec\', MAKETIME(0,2,15))'],
+ ['JSON_OBJECT(\'2min16sec\', MAKETIME(0,2,16))'],
+ ['JSON_OBJECT(\'2min32sec\', MAKETIME(0,2,32))'],
+ ['JSON_OBJECT(\'2min59sec\', MAKETIME(0,2,59))'],
+ ['JSON_OBJECT(\'key\', MAKETIME(0,59,0))'],
+ ['JSON_OBJECT(\'key\', MAKETIME(0,0,59))'],
+ ['JSON_OBJECT(\'key\', MAKETIME(20,15,10))'],
+ ['JSON_OBJECT(\'key\', MAKETIME(21,15,10))'],
+ ['JSON_OBJECT(\'key\', MAKETIME(22,15,10))'],
+ ['JSON_OBJECT(\'oneseco\', MAKETIME(0,0,1.123))'],
+ ['JSON_OBJECT(\'oneseco\', MAKETIME(0,0,1.000123))'],
+ ['JSON_OBJECT(\'key\', MAKETIME(-20,00,00))'],
+ ['JSON_OBJECT(\'-59min\', TIME(\'-00:00:00.003\'))'],
+ ['JSON_OBJECT(\'-59min\', TIME(\'00:00:00.003\'))'],
+ ['JSON_OBJECT(\'-59min\', TIME(\'-00:59:59\'))'],
+ ['JSON_OBJECT(\'-59min\', TIME(\'-00:59:59.0003\'))'],
+ ['JSON_OBJECT(\'-1hr\', MAKETIME(-1,00,00))'],
+ ['JSON_OBJECT(\'-2hr\', MAKETIME(-2,00,00))'],
+ ['JSON_OBJECT(\'-1hr1sec\', MAKETIME(-1,00,1))'],
+ ['JSON_OBJECT(\'-1hr1sec\', MAKETIME(-1,00,0.1))'],
+ ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-27\'))'],
+ ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-27 01:07:08\'))'],
+ ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-27 01:07:08.123\'))'],
+ ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-27 01:07:08.000456\'))'],
+ ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-28\'))'],
+ ['JSON_OBJECT(\'key\', TIMESTAMP(\'2014-12-29\'))'],
+ ['JSON_OBJECT(\'key\', TIMESTAMP(\'2003-12-31 12:00:00\'))'],
+ ['JSON_OBJECT(\'key\', TIMESTAMP(\'2003-12-31 12:00:00.123\'))'],
+ ['JSON_OBJECT(\'key\', UNIX_TIMESTAMP(\'2015-11-13 10:20:19.012\'))'],
+ ], function(test, event) { // caution here , don't use arrow function
+ // JSON from MySQL client has different whitespace than JSON.stringify
+ // Therefore, parse and perform deep equality
+ event.rows.forEach((row, index) => {
+ // test.deepEqual does not work when comparison objects exceed 65536 bytes
+ // Perform alternative assertions for these large cases
+ const expected = JSON.parse(this[index].col0);
+ const actual = JSON.parse(row.col0);
+ if (this[index].col0.length > 65536) {
+ // Large cases are either array or object
+ if (expected instanceof Array) {
+ test.strictEqual(expected.length, actual.length);
+ for (let i = 0; i < expected.length; i++) {
+ test.deepEqual(expected[i], actual[i]);
+ }
+ } else {
+ const expectedKeys = Object.keys(expected);
+ const actualKeys = Object.keys(actual);
+ test.strictEqual(expectedKeys.length, actualKeys.length);
+ test.deepEqual(expectedKeys, actualKeys);
+ for (let j = 0; j < expectedKeys.length; j++) {
+ test.deepEqual(expected[expectedKeys[j]], actual[expectedKeys[j]]);
+ }
}
} else {
- var expectedKeys = Object.keys(expected);
- var actualKeys = Object.keys(actual);
- test.strictEqual(expectedKeys.length, actualKeys.length);
- test.deepEqual(expectedKeys, actualKeys);
- for (var j = 0; j < expectedKeys.length; j++) {
- test.deepEqual(expected[expectedKeys[j]], actual[expectedKeys[j]]);
- }
+ // Comparison objects are smaller than 65536 bytes
+ test.deepEqual(expected, actual);
}
- } else {
- // Comparison objects are smaller than 65536 bytes
- test.deepEqual(expected, actual);
- }
- }.bind(this));
-}, '5.7.8');
+ });
+ });
+});