From 57c45a0ee2bb6229ce08fea02210c1cb99d75cb2 Mon Sep 17 00:00:00 2001 From: StoneT2000 Date: Wed, 24 Jul 2019 14:09:45 +0800 Subject: [PATCH] Added nodejs connector and example usage of nodejs --- src/connector/nodejs/nodetaos/cinterface.js | 332 ++++++++ src/connector/nodejs/nodetaos/connection.js | 66 ++ src/connector/nodejs/nodetaos/constants.js | 29 + src/connector/nodejs/nodetaos/cursor.js | 131 +++ src/connector/nodejs/nodetaos/error.js | 80 ++ src/connector/nodejs/package-lock.json | 900 ++++++++++++++++++++ src/connector/nodejs/package.json | 31 + src/connector/nodejs/readme.md | 233 +++++ src/connector/nodejs/tdengine.js | 4 + src/connector/nodejs/test/test.js | 54 ++ tests/examples/nodejs/node-example.js | 129 +++ 11 files changed, 1989 insertions(+) create mode 100644 src/connector/nodejs/nodetaos/cinterface.js create mode 100644 src/connector/nodejs/nodetaos/connection.js create mode 100644 src/connector/nodejs/nodetaos/constants.js create mode 100644 src/connector/nodejs/nodetaos/cursor.js create mode 100644 src/connector/nodejs/nodetaos/error.js create mode 100644 src/connector/nodejs/package-lock.json create mode 100644 src/connector/nodejs/package.json create mode 100644 src/connector/nodejs/readme.md create mode 100644 src/connector/nodejs/tdengine.js create mode 100644 src/connector/nodejs/test/test.js create mode 100644 tests/examples/nodejs/node-example.js diff --git a/src/connector/nodejs/nodetaos/cinterface.js b/src/connector/nodejs/nodetaos/cinterface.js new file mode 100644 index 0000000000..dc9fc2f4f1 --- /dev/null +++ b/src/connector/nodejs/nodetaos/cinterface.js @@ -0,0 +1,332 @@ +const ref = require('ref'); +const ffi = require('ffi'); +const ArrayType = require('ref-array'); +const Struct = require('ref-struct'); +const fieldTypes = require('./constants'); +const errors = require ('./error') + +module.exports = CTaosInterface; + +function convertMillisecondsToDatetime(time) { + return new Date(time); +} +function convertMicrosecondsToDatetime(time) { + return new Date(time * 0.001); +} + +function convertTimestamp(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + timestampConverter = convertMillisecondsToDatetime; + if (micro == true) { + timestampConverter = convertMicrosecondsToDatetime; + } + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = []; + let currOffset = 0; + while (currOffset < data.length) { + let queue = []; + let time = 0; + for (let i = currOffset; i < currOffset + nbytes; i++) { + queue.push(data[i]); + if (data[i] == 0) { + break; + } + } + for (let i = queue.length - 1; i >= 0; i--) { + time += queue[i] * Math.pow(16, i * 2); + } + currOffset += nbytes; + res.push(timestampConverter(time)); + } + return res; +} +function convertBool(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = new Array(data.length); + for (let i = 0; i < data.length; i++) { + if (data[i] == 0) { + res[i] = false; + } + else { + res[i] = true; + } + } + return res; +} +function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = []; + let currOffset = 0; + while (currOffset < data.length) { + res.push(data.readIntLE(currOffset,1)); + currOffset += nbytes; + } + return res; +} +function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = []; + let currOffset = 0; + while (currOffset < data.length) { + res.push(data.readIntLE(currOffset,2)); + currOffset += nbytes; + } + return res; +} +function convertInt(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = []; + let currOffset = 0; + while (currOffset < data.length) { + res.push(data.readInt32LE(currOffset)); + currOffset += nbytes; + } + return res; +} +function readBigInt64LE(buffer, offset = 0) { + const first = buffer[offset]; + const last = buffer[offset + 7]; + if (first === undefined || last === undefined) + boundsError(offset, buffer.length - 8); + + const val = buffer[offset + 4] + buffer[offset + 5] * 2 ** 8 + buffer[offset + 6] * 2 ** 16 + (last << 24); // Overflow + return ((BigInt(val) << 32n) + BigInt(first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24)); +} +function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = []; + let currOffset = 0; + while (currOffset < data.length) { + res.push(BigInt(data.readInt64LE(currOffset))); + currOffset += nbytes; + } + return res; +} +function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = []; + let currOffset = 0; + while (currOffset < data.length) { + res.push(parseFloat(data.readFloatLE(currOffset).toFixed(7))); + currOffset += nbytes; + } + return res; +} +function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = []; + let currOffset = 0; + while (currOffset < data.length) { + res.push(parseFloat(data.readDoubleLE(currOffset).toFixed(16))); + currOffset += nbytes; + } + return res; +} +function convertBinary(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = []; + let currOffset = 0; + while (currOffset < data.length) { + let dataEntry = data.slice(currOffset, currOffset + nbytes); //one entry in a row under a column; + res.push(ref.readCString(dataEntry)); + currOffset += nbytes; + } + return res; +} +function convertNchar(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { + data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); + let res = []; + let currOffset = 0; + //every 4; + while (currOffset < data.length) { + let dataEntry = data.slice(currOffset, currOffset + nbytes); //one entry in a row under a column; + res.push(dataEntry.toString("utf16le").replace(/\u0000/g, "")); + currOffset += nbytes; + } + return res; +} + +//Object with all the relevant converters from pblock data to javascript data +let convertFunctions = { + [fieldTypes.C_BOOL] : convertBool, + [fieldTypes.C_TINYINT] : convertTinyint, + [fieldTypes.C_SMALLINT] : convertSmallint, + [fieldTypes.C_INT] : convertInt, + [fieldTypes.C_BIGINT] : convertBigint, + [fieldTypes.C_FLOAT] : convertFloat, + [fieldTypes.C_DOUBLE] : convertDouble, + [fieldTypes.C_BINARY] : convertBinary, + [fieldTypes.C_TIMESTAMP] : convertTimestamp, + [fieldTypes.C_NCHAR] : convertNchar +} + +// Define TaosField structure +var char_arr = ArrayType(ref.types.char); +var TaosField = Struct({ + 'name': char_arr, + }); +TaosField.fields.name.type.size = 64; +TaosField.defineProperty('bytes', ref.types.short); +TaosField.defineProperty('type', ref.types.char); + +//The C interface with the Taos TSDB +function CTaosInterface (config = null, pass = false) { + ref.types.char_ptr = ref.refType(ref.types.char); + ref.types.void_ptr = ref.refType(ref.types.void); + /*Declare a bunch of functions first*/ + this.libtaos = ffi.Library('libtaos', { + 'taos_options': [ ref.types.int, [ ref.types.int , ref.types.void_ptr ] ], + 'taos_init': [ ref.types.void, [ ] ], + //TAOS *taos_connect(char *ip, char *user, char *pass, char *db, int port) + 'taos_connect': [ ref.types.void_ptr, [ ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.int ] ], + //void taos_close(TAOS *taos) + 'taos_close': [ ref.types.void, [ ref.types.void_ptr ] ], + //TAOS_RES *taos_use_result(TAOS *taos); + 'taos_use_result': [ ref.types.void_ptr, [ ref.types.void_ptr ] ], + //int taos_query(TAOS *taos, char *sqlstr) + 'taos_query': [ ref.types.int, [ ref.types.void_ptr, ref.types.char_ptr ] ], + //int taos_affected_rows(TAOS *taos) + 'taos_affected_rows': [ ref.types.int, [ ref.types.void_ptr] ], + //int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows) + 'taos_fetch_block': [ ref.types.int, [ ref.types.void_ptr, ref.types.void_ptr] ], + //int taos_result_precision(TAOS_RES *res) + 'taos_result_precision': [ ref.types.int, [ ref.types.void_ptr ] ], + //void taos_free_result(TAOS_RES *res) + 'taos_free_result': [ ref.types.void, [ ref.types.void_ptr] ], + //int taos_field_count(TAOS *taos) + 'taos_field_count': [ ref.types.int, [ ref.types.void_ptr ] ], + //TAOS_FIELD *taos_fetch_fields(TAOS_RES *res) + 'taos_fetch_fields': [ ref.refType(TaosField), [ ref.types.void_ptr ] ], + //int taos_errno(TAOS *taos) + 'taos_errno': [ ref.types.int, [ ref.types.void_ptr] ], + //char *taos_errstr(TAOS *taos) + 'taos_errstr': [ ref.types.char, [ ref.types.void_ptr] ] + }); + if (pass == false) { + if (config == null) { + //check this buffer + this._config = ref.alloc(ref.types.char_ptr, ref.NULL); + } + else { + try { + this._config = ref.allocCString(config);; + } + catch(err){ + throw "Attribute Error: config is expected as a str"; + } + } + if (config != null) { + this.libtaos.taos_options(3, this._config); + } + this.libtaos.taos_init(); + } +} +CTaosInterface.prototype.config = function config() { + return this._config; + } +CTaosInterface.prototype.connect = function connect(host=null, user="root", password="taosdata", db=null, port=0) { + let _host,_user,_password,_db,_port; + try { + _host = host != null ? ref.allocCString(host) : ref.alloc(ref.types.char_ptr, ref.NULL); + } + catch(err) { + throw "Attribute Error: host is expected as a str"; + } + try { + _user = ref.allocCString(user) + } + catch(err) { + throw "Attribute Error: user is expected as a str"; + } + try { + _password = ref.allocCString(password); + } + catch(err) { + throw "Attribute Error: password is expected as a str"; + } + try { + _db = db != null ? ref.allocCString(db) : ref.alloc(ref.types.char_ptr, ref.NULL); + } + catch(err) { + throw "Attribute Error: db is expected as a str"; + } + try { + _port = ref.alloc(ref.types.int, port); + } + catch(err) { + throw TypeError("port is expected as an int") + } + let connection = this.libtaos.taos_connect(_host, _user, _password, _db, _port); + if (ref.isNull(connection)) { + throw new errors.TDError('Failed to connect to TDengine'); + } + else { + console.log('Successfully connected to TDengine'); + } + return connection; +} +CTaosInterface.prototype.close = function close(connection) { + this.libtaos.taos_close(connection); + console.log("Connection is closed"); +} +CTaosInterface.prototype.query = function query(connection, sql) { + let res = this.libtaos.taos_query(connection, ref.allocCString(sql)); + return res; +} +CTaosInterface.prototype.affectedRows = function affectedRows(connection) { + return this.libtaos.taos_affected_rows(connection); +} +CTaosInterface.prototype.useResult = function useResult(connection) { + let result = this.libtaos.taos_use_result(connection); + let fields = []; + let pfields = this.fetchFields(result); + if (ref.isNull(pfields) == false) { + let fullpfields = ref.reinterpret(pfields, this.fieldsCount(connection) * 68, 0); + for (let i = 0; i < fullpfields.length; i += 68) { + //0 - 63 = name //64 - 65 = bytes, 66 - 67 = type + fields.push( { + name: ref.readCString(ref.reinterpret(fullpfields,64,i)), + bytes: fullpfields[i + 64], + type: fullpfields[i + 66] + }) + } + } + return {result:result, fields:fields} +} +CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) { + let pblock = ref.ref(ref.ref(ref.NULL)); + let num_of_rows = this.libtaos.taos_fetch_block(result, pblock) + if (num_of_rows == 0) { + return {block:null, num_of_rows:0}; + } + let isMicro = (this.libtaos.taos_result_precision(result) == fieldTypes.C_TIMESTAMP_MICRO) + let blocks = new Array(fields.length); + blocks.fill(null); + num_of_rows = Math.abs(num_of_rows); + let offset = 0; + for (let i = 0; i < fields.length; i++) { + + if (!convertFunctions[fields[i]['type']] ) { + throw new errors.DatabaseError("Invalid data type returned from database"); + } + let data = ref.reinterpret(pblock.deref().deref(), fields[i]['bytes'], offset); + blocks[i] = convertFunctions[fields[i]['type']](pblock, num_of_rows, fields[i]['bytes'], offset, isMicro); + offset += fields[i]['bytes'] * num_of_rows; + } + return {blocks: blocks, num_of_rows:Math.abs(num_of_rows)} +} +CTaosInterface.prototype.freeResult = function freeResult(result) { + this.libtaos.taos_free_result(result); + result = null; +} +CTaosInterface.prototype.fieldsCount = function fieldsCount(connection) { + return this.libtaos.taos_field_count(connection); +} +CTaosInterface.prototype.fetchFields = function fetchFields(result) { + return this.libtaos.taos_fetch_fields(result); +} +CTaosInterface.prototype.errno = function errno(connection) { + return this.libtaos.taos_errno(connection); +} +CTaosInterface.prototype.errStr = function errStr(connection) { + return (this.libtaos.taos_errstr(connection)); +} diff --git a/src/connector/nodejs/nodetaos/connection.js b/src/connector/nodejs/nodetaos/connection.js new file mode 100644 index 0000000000..d34aa92da0 --- /dev/null +++ b/src/connector/nodejs/nodetaos/connection.js @@ -0,0 +1,66 @@ +const TDengineCursor = require('./cursor') +const CTaosInterface = require('./cinterface') +module.exports = TDengineConnection; + +/* + * TDengine Connection object + * @param {Object.} options - Options for configuring the connection with TDengine + * @return {TDengineConnection} + * + * + */ +function TDengineConnection(options) { + this._conn = null; + this._host = null; + this._user = "root"; //The default user + this._password = "taosdata"; //The default password + this._database = null; + this._port = 0; + this._config = null; + this._chandle = null; + this.config(options) + return this; +} + +TDengineConnection.prototype.config = function config(options) { + if (options['host']) { + this._host = options['host']; + } + if (options['user']) { + this._user = options['user']; + } + if (options['password']) { + this._password = options['password']; + } + if (options['database']) { + this._database = options['database']; + } + if (options['port']) { + this._port = options['port']; + } + if (options['config']) { + this._config = options['config']; + } + this._chandle = new CTaosInterface(this._config); + this._conn = this._chandle.connect(this._host, this._user, this._password, this._database, this._port); +} + +TDengineConnection.prototype.close = function close() { + return this._chandle.close(this._conn); +} +TDengineConnection.prototype.cursor = function cursor() { + //Pass the connection object to the cursor + return new TDengineCursor(this); +} +TDengineConnection.prototype.commit = function commit() { + return this; +} +TDengineConnection.prototype.rollback = function rollback() { + return this; +} +TDengineConnection.prototype.clear_result_set = function clear_result_set() { + var result = this._chandle.useResult(this._conn).result; + if (result) { + this._chandle.freeResult(result) + } +} diff --git a/src/connector/nodejs/nodetaos/constants.js b/src/connector/nodejs/nodetaos/constants.js new file mode 100644 index 0000000000..bb65b6d91c --- /dev/null +++ b/src/connector/nodejs/nodetaos/constants.js @@ -0,0 +1,29 @@ +/* + * TDengine Field Types + * + * + */ +module.exports = { + // Type Code + C_NULL : 0, + C_BOOL : 1, + C_TINYINT : 2, + C_SMALLINT : 3, + C_INT : 4, + C_BIGINT : 5, + C_FLOAT : 6, + C_DOUBLE : 7, + C_BINARY : 8, + C_TIMESTAMP : 9, + C_NCHAR : 10, + // NULL value definition + // NOTE: These values should change according to C definition in tsdb.h + C_BOOL_NULL : 0x02, + C_TINYINT_NULL : -128, + C_SMALLINT_NULL : -32768, + C_INT_NULL : -2147483648, + C_BIGINT_NULL : -9223372036854775808, + + C_TIMESTAMP_MILLI : 0, + C_TIMESTAMP_MICRO : 1, +} diff --git a/src/connector/nodejs/nodetaos/cursor.js b/src/connector/nodejs/nodetaos/cursor.js new file mode 100644 index 0000000000..8089ab13e2 --- /dev/null +++ b/src/connector/nodejs/nodetaos/cursor.js @@ -0,0 +1,131 @@ +const CTaosInterface = require('./cinterface') +const errors = require ('./error') +module.exports = TDengineCursor; + +function TDengineCursor(connection=null) { + this._description = null; + this._rowcount = -1; + this._connection = null; + this._result = null; + this._fields = null; + this.data = null; + this.fieldNames = null; + this.chandle = new CTaosInterface(null, true); //pass through, just need library loaded. + if (connection != null) { + this._connection = connection + } + +} +TDengineCursor.prototype.description = function description() { + return this._description; +} +TDengineCursor.prototype.rowcount = function rowcount() { + return this._rowcount; +} +TDengineCursor.prototype.callproc = function callproc() { + return; +} +TDengineCursor.prototype.close = function close() { + if (this._connection == null) { + return false; + } + this._connection.clear_result_set(); + this._reset_result(); + this._connection = null; + return true; +} +TDengineCursor.prototype.execute = function execute(operation, params=null) { + if (operation == undefined) { + return null; + } + if (this._connection == null) { + throw new errors.ProgrammingError('Cursor is not connected'); + } + this._connection.clear_result_set(); + this._reset_result(); + + let stmt = operation; + if (params != null) { + //why pass? + } + res = this.chandle.query(this._connection._conn, stmt); + if (res == 0) { + let fieldCount = this.chandle.fieldsCount(this._connection._conn); + if (fieldCount == 0) { + return this.chandle.affectedRows(this._connection._conn); //return num of affected rows, common with insert, use statements + } + else { + let resAndField = this.chandle.useResult(this._connection._conn, fieldCount) + this._result = resAndField.result; + this._fields = resAndField.fields; + this.fieldNames = resAndField.fields.map(fieldData => fieldData.name); + return this._handle_result(); //return a pointer + } + } + else { + throw new errors.ProgrammingError(this.chandle.errStr(this._connection._conn)) + } + + +} +TDengineCursor.prototype.executemany = function executemany() { + +} +TDengineCursor.prototype.fetchone = function fetchone() { + +} +TDengineCursor.prototype.fetchmany = function fetchmany() { + +} +TDengineCursor.prototype.fetchall = function fetchall() { + if (this._result == null || this._fields == null) { + throw new errors.OperationalError("Invalid use of fetchall, either result or fields from query are null"); + } + let data = []; + this._rowcount = 0; + let k = 0; + while(true) { + k+=1; + let blockAndRows = this.chandle.fetchBlock(this._result, this._fields); + let block = blockAndRows.blocks; + let num_of_rows = blockAndRows.num_of_rows; + + if (num_of_rows == 0) { + break; + } + this._rowcount += num_of_rows; + for (let i = 0; i < num_of_rows; i++) { + data.push([]); + for (let j = 0; j < this._fields.length; j++) { + data[data.length-1].push(block[j][i]); + } + } + } + this._connection.clear_result_set(); + this.data = data; + return data; //data[i] returns the ith row, with all the data +} +TDengineCursor.prototype.nextset = function nextset() { + return; +} +TDengineCursor.prototype.setinputsize = function setinputsize() { + return; +} +TDengineCursor.prototype.setoutputsize = function setoutputsize(size, column=null) { + return; +} +TDengineCursor.prototype._reset_result = function _reset_result() { + this._description = null; + this._rowcount = -1; + this._result = null; + this._fields = null; + this.data = null; + this.fieldNames = null; +} +TDengineCursor.prototype._handle_result = function _handle_result() { + this._description = []; + for (let field of this._fields) { + this._description.push([field.name, field.type, null, null, null, null, false]); + } + return this._result; +} diff --git a/src/connector/nodejs/nodetaos/error.js b/src/connector/nodejs/nodetaos/error.js new file mode 100644 index 0000000000..d5cc39d199 --- /dev/null +++ b/src/connector/nodejs/nodetaos/error.js @@ -0,0 +1,80 @@ +/* +Classes for exceptions +Note: All exceptions thrown by Node.js or the JavaScript runtime will be instances of Error. +https://nodejs.org/api/errors.html#errors_exceptions_vs_errors + +*/ + +class TDError extends Error { + constructor(args) { + super(args) + this.name = "TDError"; + } +} +class Warning extends Error { + // Exception raised for important warnings like data truncations while inserting. + constructor(args) { + super(args) + this.name = "Warning"; + } +} +class InterfaceError extends TDError { + // Exception raised for errors that are related to the database interface rather than the database itself. + constructor(args) { + super(args) + this.name = "TDError.InterfaceError"; + } +} +class DatabaseError extends TDError { + // Exception raised for errors that are related to the database. + constructor(args) { + super(args) + this.name = "TDError.DatabaseError"; + } +} +class DataError extends DatabaseError { + // Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range. + constructor(args) { + super(args) + this.name = "TDError.DatabaseError.DataError"; + } +} +class OperationalError extends DatabaseError { + // Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer + constructor(args) { + super(args) + this.name = "TDError.DatabaseError.OperationalError"; + } +} +class IntegrityError extends DatabaseError { + // Exception raised when the relational integrity of the database is affected. + constructor(args) { + super(args) + this.name = "TDError.DatabaseError.IntegrityError"; + } +} +class InternalError extends DatabaseError { + // Exception raised when the database encounters an internal error. + constructor(args) { + super(args) + this.name = "TDError.DatabaseError.InternalError"; + } +} +class ProgrammingError extends DatabaseError { + // Exception raised for programming errors. + constructor(args) { + super(args) + this.name = "TDError.DatabaseError.ProgrammingError"; + } +} +class NotSupportedError extends DatabaseError { + // Exception raised in case a method or database API was used which is not supported by the database. + constructor(args) { + super(args) + this.name = "TDError.DatabaseError.NotSupportedError"; + } +} + +module.exports = { + TDError, Warning, InterfaceError, DatabaseError, DataError, OperationalError, IntegrityError, InternalError, ProgrammingError, NotSupportedError +}; diff --git a/src/connector/nodejs/package-lock.json b/src/connector/nodejs/package-lock.json new file mode 100644 index 0000000000..230689532a --- /dev/null +++ b/src/connector/nodejs/package-lock.json @@ -0,0 +1,900 @@ +{ + "name": "tdconnector", + "version": "1.0.5", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "array-index": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-index/-/array-index-1.0.0.tgz", + "integrity": "sha1-7FanSe4QPk4Ix5C5w1PfFgVbl/k=", + "requires": { + "debug": "^2.2.0", + "es6-symbol": "^3.0.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "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=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "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=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chownr": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", + "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==" + }, + "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=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "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=" + }, + "ctypes": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/ctypes/-/ctypes-0.0.1.tgz", + "integrity": "sha1-/C7F5ZmRwG9mNksh7yxy/SkQnLE=", + "requires": { + "array-index": "^1.0.0", + "debug": "^2.2.0", + "es6-weak-map": "^2.0.1", + "ffi": "^2.0.0", + "function-class": "^1.1.0", + "ref": "^1.3.1", + "ref-array": "^1.1.2", + "ref-struct": "^1.0.2", + "setprototypeof": "^1.0.0" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.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=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "env-paths": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=" + }, + "es5-ext": { + "version": "0.10.50", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", + "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "ffi": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ffi/-/ffi-2.3.0.tgz", + "integrity": "sha512-vkPA9Hf9CVuQ5HeMZykYvrZF2QNJ/iKGLkyDkisBnoOOFeFXZQhUPxBARPBIZMJVulvBI2R+jgofW03gyPpJcQ==", + "requires": { + "bindings": "~1.2.0", + "debug": "2", + "nan": "2", + "ref": "1", + "ref-struct": "1" + }, + "dependencies": { + "bindings": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" + } + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "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==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz", + "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==", + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-class": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/function-class/-/function-class-1.1.0.tgz", + "integrity": "sha1-yCK8J5x4py11je/y4kzXwPtYWLo=", + "requires": { + "es6-symbol": "^3.0.2", + "function-name": "^1.0.0", + "setprototypeof": "^1.0.0" + } + }, + "function-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/function-name/-/function-name-1.0.0.tgz", + "integrity": "sha1-r+4YP9eh3JIi1fZae/htpYNd90U=", + "requires": { + "bindings": "^1.2.1", + "nan": "2" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "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" + } + }, + "graceful-fs": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", + "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "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==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "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==" + }, + "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=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "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=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "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==" + }, + "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=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "node-gyp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.0.2.tgz", + "integrity": "sha512-sNcb5O7eJ9XiNAhWZ/UE2bWsBJn3Jb7rayMqMP4wjenlr1DwzZxUmbtmIrl04EU0p5fN2rU9WIDV+u0EbsI8oQ==", + "requires": { + "env-paths": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^4.4.8", + "which": "1" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "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=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "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==" + }, + "psl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", + "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "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" + } + }, + "ref": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ref/-/ref-1.3.5.tgz", + "integrity": "sha512-2cBCniTtxcGUjDpvFfVpw323a83/0RLSGJJY5l5lcomZWhYpU2cuLdsvYqMixvsdLJ9+sTdzEkju8J8ZHDM2nA==", + "requires": { + "bindings": "1", + "debug": "2", + "nan": "2" + } + }, + "ref-array": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ref-array/-/ref-array-1.2.0.tgz", + "integrity": "sha1-u6hwFS1O4KvtFCGwYjkvCwGEKQw=", + "requires": { + "array-index": "1", + "debug": "2", + "ref": "1" + } + }, + "ref-struct": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ref-struct/-/ref-struct-1.1.0.tgz", + "integrity": "sha1-XV7mWtQc78Olxf60BYcmHkee3BM=", + "requires": { + "debug": "2", + "ref": "1" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "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": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "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": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "tar": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", + "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.5", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "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=" + }, + "type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.1.tgz", + "integrity": "sha512-MAM5dBMJCJNKs9E7JXo4CXRAansRfG0nlJxW7Wf6GZzSOvH31zClSaHdIMWLehe/EGMBkqeC55rrkaOr5Oo7Nw==" + }, + "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==", + "requires": { + "punycode": "^2.1.0" + } + }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "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.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } +} diff --git a/src/connector/nodejs/package.json b/src/connector/nodejs/package.json new file mode 100644 index 0000000000..5484fd1d2d --- /dev/null +++ b/src/connector/nodejs/package.json @@ -0,0 +1,31 @@ +{ + "name": "td-connector", + "version": "1.0.5", + "description": "A Node.js connector for TDengine.", + "main": "tdengine.js", + "scripts": { + "test": "node test/test.js" + }, + "keywords": [ + "TDengine", + "TAOS Data", + "Time Series Database", + "Connector" + ], + "author": "StoneT2000", + "license": "AGPL-3.0", + "dependencies": { + "ffi": "^2.3.0", + "node-gyp": "^5.0.2", + "ref": "^1.3.5", + "ref-array": "^1.2.0" + }, + "homepage": "https://github.com/taosdata/tdengine", + "bugs": { + "url": "https://github.com/taosdata/tdengine/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/taosdata/tdengine.git" + } +} diff --git a/src/connector/nodejs/readme.md b/src/connector/nodejs/readme.md new file mode 100644 index 0000000000..e45443cfa0 --- /dev/null +++ b/src/connector/nodejs/readme.md @@ -0,0 +1,233 @@ +# TDengine Node.js connector +This is the Node.js library that lets you connect to [TDengine](https://www.github.com/taosdata/tdengine). + +## Installation + +To get started, just type in the following to install the connector through [npm](https://www.npmjs.com/) + +```cmd +npm install td-connector +``` + +To interact with TDengine, we make use of the [node-gyp[(https://github.com/nodejs/node-gyp)] library. To install, you will need to install the following depending on platform (the following instructions are quoted from node-gyp) + +### On Unix + +- `python` (`v2.7` recommended, `v3.x.x` is **not** supported) +- `make` +- A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org) + +### On macOS + +- `python` (`v2.7` recommended, `v3.x.x` is **not** supported) (already installed on macOS) + +- Xcode + + - You also need to install the + + ``` + Command Line Tools + ``` + + via Xcode. You can find this under the menu + + ``` + Xcode -> Preferences -> Locations + ``` + + (or by running + + ``` + xcode-select --install + ``` + + in your Terminal) + + - This step will install `gcc` and the related toolchain containing `make` + +### On Windows + +#### Option 1 + +Install all the required tools and configurations using Microsoft's [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) using `npm install --global --production windows-build-tools` from an elevated PowerShell or CMD.exe (run as Administrator). + +#### Option 2 + +Install tools and configuration manually: + +- Install Visual C++ Build Environment: [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) (using "Visual C++ build tools" workload) or [Visual Studio 2017 Community](https://visualstudio.microsoft.com/pl/thank-you-downloading-visual-studio/?sku=Community) (using the "Desktop development with C++" workload) +- Install [Python 2.7](https://www.python.org/downloads/) (`v3.x.x` is not supported), and run `npm config set python python2.7` (or see below for further instructions on specifying the proper Python version and path.) +- Launch cmd, `npm config set msvs_version 2017` + +If the above steps didn't work for you, please visit [Microsoft's Node.js Guidelines for Windows](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules) for additional tips. + +To target native ARM64 Node.js on Windows 10 on ARM, add the components "Visual C++ compilers and libraries for ARM64" and "Visual C++ ATL for ARM64". + +## Usage + +To use the connector, first request the library ```td-connector```. Running the function ```taos.connect``` with the connection options passed in as an object will return a TDengine connection object. A cursor needs to be intialized in order to interact with TDengine from node. + +```javascript +const taos = require('td-connector'); +var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0}) +var c1 = conn.cursor(); // Initializing a new cursor +``` + +We can now start executing queries through the ```cursor.execute``` function. + +```javascript +c1.execute('show databases;') +``` + +We can get the results of the queries by doing the following + +```javascript +var data = c1.fetchall(); +console.log(c1.fieldNames); // Returns the names of the columns/fields +console.log(data); // Logs all the data from the query as an array of arrays, each of which represents a row and data[row_number] is sorted in order of the fields +``` + +## Example + +The following is an example use of the connector showing how to make a table with weather data, insert random data, and then retrieve it. + +```javascript +// Get the td-connector package +const taos = require('td-connector'); + +/* We will connect to TDengine by passing an object comprised of connection options to taos.connect and store the + * connection to the variable conn + */ +/* + * Connection Options + * host: the host to connect to + * user: the use to login as + * password: the password for the above user to login + * config: the location of the taos.cfg file, by default it is in /etc/taos + * port: the port we connect through + */ +var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0}); + +// Initialize our TDengineCursor, which we use to interact with TDengine +var c1 = conn.cursor(); + +// c1.execute(query) will execute the query +// Let's create a database named db +try { + c1.execute('create database db;'); +} +catch(err) { + conn.close(); + throw err; +} + +// Now we will use database db +try { + c1.execute('use db;'); +} +catch (err) { + conn.close(); + throw err; +} + +// Let's create a table called weather +// which stores some weather data like humidity, AQI (air quality index), temperature, and some notes as text +try { + c1.execute('create table if not exists weather (ts timestamp, humidity smallint, aqi int, temperature float, notes binary(30));'); +} +catch (err) { + conn.close(); + throw err; +} + +// Let's get the description of the table weather +try { + c1.execute('describe db.weather'); +} +catch (err) { + conn.close(); + throw err; +} + +// To get results, we run the function c1.fetchall() +// It only returns the query results as an array of result rows, but also stores the latest results in c1.data +try { + var tableDesc = c1.fetchall(); // The description variable here is equal to c1.data; + console.log(tableDesc); +} +catch (err) { + conn.close(); + throw err; +} + +// Let's try to insert some random generated data to test with + +let stime = new Date(); +let interval = 1000; + +// Timestamps must be in the form of "YYYY-MM-DD HH:MM:SS.MMM" if they are in milliseconds +// "YYYY-MM-DD HH:MM:SS.MMMMMM" if they are in microseconds +// Thus, we create the following function to convert a javascript Date object to the correct formatting +function convertDateToTS(date) { + let tsArr = date.toISOString().split("T") + return "\"" + tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length-1) + "\""; +} + +try { + for (let i = 0; i < 10000; i++) { + stime.setMilliseconds(stime.getMilliseconds() + interval); + let insertData = [convertDateToTS(stime), + parseInt(Math.random()*100), + parseInt(Math.random()*300), + parseFloat(Math.random()*10 + 30), + "\"random note!\""]; + c1.execute('insert into db.weather values(' + insertData.join(',') + ' );'); + } +} +catch (err) { + conn.close(); + throw err; +} + +// Now let's look at our newly inserted data +var retrievedData; +try { + c1.execute('select * from db.weather;') + retrievedData = c1.fetchall(); + + // c1.fieldNames stores the names of each column retrieved + console.log(c1.fieldNames); + console.log(retrievedData); + // timestamps retrieved are always JS Date Objects + // Numbers are numbers, big ints are big ints, and strings are strings +} +catch (err) { + conn.close(); + throw err; +} + +// Let's try running some basic functions +try { + c1.execute('select count(*), avg(temperature), max(temperature), min(temperature), stddev(temperature) from db.weather;') + c1.fetchall(); + console.log(c1.fieldNames); + console.log(c1.data); +} +catch(err) { + conn.close(); + throw err; +} + +conn.close(); + +// Feel free to fork this repository or copy this code and start developing your own apps and backends with NodeJS and TDengine! + +``` + +## Contributing to TDengine + +Please follow the [contribution guidelines](https://github.com/taosdata/TDengine/blob/master/CONTRIBUTING.md) to contribute to the project. + +## License + +[GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html) \ No newline at end of file diff --git a/src/connector/nodejs/tdengine.js b/src/connector/nodejs/tdengine.js new file mode 100644 index 0000000000..aa296279d5 --- /dev/null +++ b/src/connector/nodejs/tdengine.js @@ -0,0 +1,4 @@ +var TDengineConnection = require('./nodetaos/connection.js') +module.exports.connect = function (connection=null) { + return new TDengineConnection(connection); +} diff --git a/src/connector/nodejs/test/test.js b/src/connector/nodejs/test/test.js new file mode 100644 index 0000000000..6cf0178d3a --- /dev/null +++ b/src/connector/nodejs/test/test.js @@ -0,0 +1,54 @@ +const taos = require('td-connector'); +var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0}); +var c1 = conn.cursor(); +let stime = new Date(); +let interval = 1000; + +// Timestamps must be in the form of "YYYY-MM-DD HH:MM:SS.MMM" if they are in milliseconds +// "YYYY-MM-DD HH:MM:SS.MMMMMM" if they are in microseconds +// Thus, we create the following function to convert a javascript Date object to the correct formatting +function convertDateToTS(date) { + let tsArr = date.toISOString().split("T") + return "\"" + tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length-1) + "\""; +} +function R(l,r) { + return Math.random() * (r - l) - r; +} +function randomBool() { + if (Math.random() < 0.5) { + return true; + } + return false; +} +c1.execute('create database td_connector_test;'); +c1.execute('use td_connector_test;') +c1.execute('create table if not exists all_types (ts timestamp, _int int, _bigint bigint, _float float, _double double, _binary binary(40), _smallint smallint, _tinyint tinyint, _bool bool, _nchar nchar(40));'); + +for (let i = 0; i < 10000; i++) { + stime.setMilliseconds(stime.getMilliseconds() + interval); + let insertData = [convertDateToTS(stime), // Timestamp + parseInt( R(-Math.pow(2,31) + 1 , Math.pow(2,31) - 1) ), // Int + parseInt( R(-Math.pow(2,31) + 1 , Math.pow(2,31) - 1) ), // BigInt + parseFloat( R(-3.4E38, 3.4E38) ), // Float + parseFloat( R(-1.7E308, 1.7E308) ), // Double + "\"Long Binary\"", // Binary + parseInt( R(-32767, 32767) ), // Small Int + parseInt( R(-127, 127) ), // Tiny Int + randomBool(), + "\"Nchars 一些中文字幕\""]; // Bool + c1.execute('insert into td_connector_test.all_types values(' + insertData.join(',') + ' );'); +} + +c1.execute('select * from td_connector_test.all_types limit 10 offset 1000;'); +var d = c1.fetchall(); +console.log(c1.fieldNames); +console.log(d); + +c1.execute('select count(*), avg(_int), sum(_float), max(_bigint), min(_double) from td_connector_test.all_types;'); +var d = c1.fetchall(); +console.log(c1.fieldNames); +console.log(d); + +c1.execute('drop database td_connector_test;') + +conn.close(); diff --git a/tests/examples/nodejs/node-example.js b/tests/examples/nodejs/node-example.js new file mode 100644 index 0000000000..1fbbb7ea0e --- /dev/null +++ b/tests/examples/nodejs/node-example.js @@ -0,0 +1,129 @@ +// Get the td-connector package +const taos = require('td-connector'); + +/* We will connect to TDengine by passing an object comprised of connection options to taos.connect and store the + * connection to the variable conn + */ +/* + * Connection Options + * host: the host to connect to + * user: the use to login as + * password: the password for the above user to login + * config: the location of the taos.cfg file, by default it is in /etc/taos + * port: the port we connect through + */ +var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0}); + +// Initialize our TDengineCursor, which we use to interact with TDengine +var c1 = conn.cursor(); + +// c1.execute(query) will execute the query +// Let's create a database named db +try { + c1.execute('create database db;'); +} +catch(err) { + conn.close(); + throw err; +} + +// Now we will use database db +try { + c1.execute('use db;'); +} +catch (err) { + conn.close(); + throw err; +} + +// Let's create a table called weather +// which stores some weather data like humidity, AQI (air quality index), temperature, and some notes as text +try { + c1.execute('create table if not exists weather (ts timestamp, humidity smallint, aqi int, temperature float, notes binary(30));'); +} +catch (err) { + conn.close(); + throw err; +} + +// Let's get the description of the table weather +try { + c1.execute('describe db.weather'); +} +catch (err) { + conn.close(); + throw err; +} + +// To get results, we run the function c1.fetchall() +// It only returns the query results as an array of result rows, but also stores the latest results in c1.data +try { + var tableDesc = c1.fetchall(); // The description variable here is equal to c1.data; + console.log(tableDesc); +} +catch (err) { + conn.close(); + throw err; +} + +// Let's try to insert some random generated data to test with + +let stime = new Date(); +let interval = 1000; + +// Timestamps must be in the form of "YYYY-MM-DD HH:MM:SS.MMM" if they are in milliseconds +// "YYYY-MM-DD HH:MM:SS.MMMMMM" if they are in microseconds +// Thus, we create the following function to convert a javascript Date object to the correct formatting +function convertDateToTS(date) { + let tsArr = date.toISOString().split("T") + return "\"" + tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length-1) + "\""; +} + +try { + for (let i = 0; i < 10000; i++) { + stime.setMilliseconds(stime.getMilliseconds() + interval); + let insertData = [convertDateToTS(stime), + parseInt(Math.random()*100), + parseInt(Math.random()*300), + parseFloat(Math.random()*10 + 30), + "\"random note!\""]; + c1.execute('insert into db.weather values(' + insertData.join(',') + ' );'); + } +} +catch (err) { + conn.close(); + throw err; +} + +// Now let's look at our newly inserted data +var retrievedData; +try { + c1.execute('select * from db.weather;') + retrievedData = c1.fetchall(); + + // c1.fieldNames stores the names of each column retrieved + console.log(c1.fieldNames); + console.log(retrievedData); + // timestamps retrieved are always JS Date Objects + // Numbers are numbers, big ints are big ints, and strings are strings +} +catch (err) { + conn.close(); + throw err; +} + +// Let's try running some basic functions +try { + c1.execute('select count(*), avg(temperature), max(temperature), min(temperature), stddev(temperature) from db.weather;') + c1.fetchall(); + console.log(c1.fieldNames); + console.log(c1.data); +} +catch(err) { + conn.close(); + throw err; +} + +conn.close(); + +// Feel free to fork this repository or copy this code and start developing your own apps and backends with NodeJS and TDengine!