diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index bcdcacf..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index 911e7f0..bb36d88 100644 --- a/.gitignore +++ b/.gitignore @@ -92,4 +92,9 @@ jspm_packages # Yarn Integrity file .yarn-integrity -playground.js \ No newline at end of file +playground.js +samples.js +playground + +# .DS_Store +.DS_Store diff --git a/.idea/workspace.xml b/.idea/workspace.xml index ad84154..5ab4d6d 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,15 +2,12 @@ - - - - - + + @@ -31,228 +28,72 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - removeSpecialArgs + Cannot use + + in + \n \* \@param ops + Error in : + Error in + Error in Http.get: + createMixedContext + rec + must + replaceVariables + tem + mustache + getFromContext + ops.loffer + ops.loffe + ops.loff + ops.lof + ops.lo + ops.logger + mashape + Func + s + resolve + ReferenceError + FunctionNode + head + is not defined + LogicNodegit + logic + + ` + - @@ -388,7 +259,6 @@ - @@ -465,6 +335,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -493,16 +412,17 @@ - + + - - - - + + + + @@ -517,8 +437,102 @@ - - + + + project + + $PROJECT_DIR$ + true + + bdd + + SUITE + $PROJECT_DIR$/test/logic-node.js + + + + + + + + + project + + $PROJECT_DIR$ + true + + bdd + + TEST + $PROJECT_DIR$/test/logic-node.js + + + + + + + + + + project + + $PROJECT_DIR$ + true + + bdd + + SUITE + $PROJECT_DIR$/test/logic-node.js + + + + + + + + + project + + $PROJECT_DIR$ + true + + bdd + + SUITE + $PROJECT_DIR$/test/logic-node.js + + + + + + + + + project + + $PROJECT_DIR$ + true + + bdd + + SUITE + $PROJECT_DIR$/test/logic-node.js + + + + + + + + + @@ -527,6 +541,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -600,12 +634,34 @@ false - - + + project + + $PROJECT_DIR$ + true + + bdd + --recursive + DIRECTORY + $PROJECT_DIR$/test + false + + + + + + + + + - - + + + + + + @@ -651,52 +707,123 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + - - + - - - - - + + + + + + + - + - - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - + - - + + - + - - + + - + - - - + + - + - - + + - + - + - + - - + + - + - - + + - - + - - + + - + - - + + + - + - - + + - + - - - - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + + - - + + - - + + - + - - + + - + - - - - - + + - + - - + + - + - - + + - + - + - + - + - - + + - + - - + + - + - - + + - + - - - - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - + + - - - - - - - - + + + + - - - - + - - - + + - + - - - - - + + - + - - - + + - + - - + + - + - - - + + - + - - + + - + - - - - - + + - + - - - - - + + + - + - - - - - + + + - + - - - - - + + + - + - - + + - + - - - - - + + + - + - - + + - + - - + + - + - - + + + + + + + + + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..73b7a5c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/index.js" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 2d06c01..95bbe82 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,29 @@ +# RapidQL +[![](https://i.imgur.com/9iuHMLS.png)](http://rapidql.com) + **RapidQL** let's developer query multiple API resources at a time, combining them to create 1 unified Query. +[![NPM version](https://badge.fury.io/js/rapidql.svg)](https://www.npmjs.com/package/rapidql) [![](https://circleci.com/gh/iddogino/rapidql.svg?style=shield&circle-token=70838eabb9e7255c543594d9c12d44db3e9b979e)](https://circleci.com/gh/iddogino/rapidql) -## Installation - npm install https://github.com/iddogino/rapidql.git -g - -The `-g` flag is necessary to run from command line (see bellow). +## Full Documentation +See the [RapidQL documentation](https://docs.rapidql.com) ## Initialization -After requiring the RapidQL package, you can initialize it. You may also pass options, such as RapidAPI credentials. - - const RapidQL = require('RapidQL'); - let rql = new RapidQL({ - RapidAPI : { - projectName : '########', - apiKey: '##########' - } - }); - +After requiring the RapidQL package, you can initialize it. You may also pass options, such as RapidAPI credentials or default HTTP Parameters +```javascript +const RapidQL = require('RapidQL'); +let rql = new RapidQL({ + Http : { + X-RapidAPI-Header: '***********' + }, + RapidAPI : { + projectName : '########', + apiKey: '##########' + } +}); +``` ## Querying You can perform queries using the method rql.query(). It takes 2 arguments: @@ -27,88 +32,169 @@ You can perform queries using the method rql.query(). It takes 2 arguments: 2. *[optional]* A base context. You may use the base context for parameter passing (any parameters such as user name used in the query should be passed through the base context. The query string should be completely static). Queries return promises. If the promise rejects, the returned value will be the error message. If the query resolves, the returned value will be the query result. +```javascript +//Running this query on this base context will return the object {a:1} +rql.query(` +{ + a +} +`, { + a: 1, + b: 2 +}).then(console.log).catch(console.warn); +``` + +## Logging + +Similarly to querying, you can directly log the results of your query using the rql.log() method. It takes 2 arguments: + +1. The query string +2. *[optional]* A base context. You may use the base context for parameter passing (any parameters such as user name used in the query should be passed through the base context. The query string should be completely static). - //Running this query on this base context will return the object {a:1} - rql.query(` - { - a +Queries return promises. If the promise rejects, the returned value will be the error message. If the query resolves, the returned value will be the query result. +```javascript +//Running this query on this base context will return the object {a:1} +rql.log(` +{ + a +} +`, { + a: 1, + b: 2 +}) +``` +## HTTP Requests +With RapidQL, you can also connect to any HTTP url using Http.get(). You may also use patch, post, and put requests. +```javascript +const RapidQL = require('RapidQL'); +const rql = new RapidQL({}); + +rql.log(`{ + Http.get(url:"http://httpbin.org/ip") { + origin + } +}`); +``` + +An HTTP request in RQL can take many parameters beyond the url. The params include: + +- __url__: a fully qualified URI +- __body__: entity body for PATCH, POST and PUT requests (not usable on GET requests) +- __form__: data to pass for a multipart/form-data request (not usable on GET requests) +- __json__: a boolean that when true, sets body to JSON representation of value and adds the +- __Content-type__: application/json header (not usable on GET requests) +- __headers__: HTTP headers (default: {}) +- __bearer__: an OAuth bearer token +- __basic__: credentials for basic auth. + +### Setting Default HTTP Parameters +When initializing your RapidQL instance, you can provide default parameters for HTTP requests by supplying an Http object as an option. This Http can set default parameters for headers, bearer, and basic. These parameters will then be sent with every HTTP request. +```javascript +const RapidQL = require('RapidQL'); +const rql = new RapidQL({ + Http: { + headers : { + 'X-RapidAPI-Key' : '*****************', + 'default' : 'this header will always be sent, unless defined otherwise at time of call' + }, + basic : { + username : 'my_username', + password : 'my_password' + } } - `, { - a: 1, - b: 2 - }).then(console.log).catch(console.warn); +}); +rql.log(`{ + Http.post( + url:"http://httpbin.org/ip" + ){ + + } +}`) +``` + +### Escaping Strings +If you need to have a variable string within on of your queries (Ex/ URL parameters for an API) you're able to escape the string by using the following notation: {{variable_name}}. An example of how to use this is the following: +```javascript +const RapidQL = require('RapidQL'); +const rql = new RapidQL({}); + +rql.log(`{ + Http.post( + url:"http://httpbin.org/status/{{status_code}}" + ){ + + } +}`, { + status_code: 400 +}) +``` ## Sample queries -Get images from Instagram and then process with AWS Rekognition - - rql.query(` - { - Instagram.getUsersRecentMedia(userId:"self", accessToken:"175826345.49afbf0.885ac554935e45ac9f83d811e870211c") { - data { - caption { - text - }, - images { - standard_resolution { - url, - AWSRekognition.detectLabelsInImage(image:url, apiKey:"AKIAJELI5PIGECVCLS2Q", apiSecret : "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") { - Labels { - Name, - Confidence - } - } - } - } - } - } - } - `).then(pipe(JSON.stringify, console.log)).catch((err) => {console.warn(err)}); - - -Get friends and their profile pics - - rql.query(` - { - FacebookGraphAPI.getUsersFriends(user_id : "me", access_token:"EAACEdEose0cBAMf2uam36XJ5NZByqoq3cwiYacIba3eDkgkMhQ6kVqWbg5zLXw2LgAkZAgYQA9qRZAcYGVP527AHXakDDnF38YOZAZBnQDTQZCmKlG8ZCOFBDSZABdllBteFzzgnBFCQihxO3Vl7wuwZBrXvXLJ61JvaYWVpqoqTDcwZDZD") { - data { - name, - FacebookGraphAPI.getProfilePicture(profile_id:d, access_token:"EAACEdEose0cBAMf2uam36XJ5NZByqoq3cwiYacIba3eDkgkMhQ6kVqWbg5zLXw2LgAkZAgYQA9qRZAcYGVP527AHXakDDnF38YOZAZBnQDTQZCmKlG8ZCOFBDSZABdllBteFzzgnBFCQihxO3Vl7wuwZBrXvXLJ61JvaYWVpqoqTDcwZDZD"){ - data { - url - } - } - } - } +Get user from a database and do validation of both email and phone number +```javascript +rql.log(`{ + MySQL.public.users.find(username:input) { + email, + phoneNumber, + name, + - Telesign:Http.post( + url: 'https://telesign-telesign-score-v1.p.rapidapi.com/score/{{phoneNumber}}', + headers : { + 'Content-Type' : 'application/x-www-form-urlencoded' + }, + params : { + 'account_lifecycle_event' : 'create' + } + ){ + phone_number_risk : risk + }, + - Mailboxlayer:Http.get( + url: 'https://apilayer-mailboxlayer-v1.p.rapidapi.com/check', + headers : { + }, + params : { + smtp: '1', + catch_all: '0', + email: email, + access_key: '************************' + } + ){ + email_score:score } - `).then(pipe(JSON.stringify, console.log)).catch(console.warn); - + } +}` , { + input : 'rapidapi' +}) +``` ## DB Queries RapidQL may also be used for querying databases. Database queries and API queries may be combined to create advance data gathering logic. ### Set Up To add a database connection to your rql instance, you need to add it's connection details in the RapidQL initialization: - - const RapidQL = require('RapidQL'); - const rql = new RapidQL({ - PostgreSQL: { - Sample: { - user: 'admin', //required - database: 'compose', //required - password: '#########', //required - host: 'aws-us-east-1-portal.23.dblayer.com', // required - port: 17052, //required - max: 10, // optional - max connections - idleTimeoutMillis: 30000 // optional - how long a client is allowed to remain idle before being closed - } - } - }); +```javascript +const RapidQL = require('RapidQL'); +const rql = new RapidQL({ + PostgreSQL: { + Sample: { + user: 'admin', //required + database: 'compose', //required + password: '#########', //required + host: 'aws-us-east-1-portal.23.dblayer.com', // required + port: 17052, //required + max: 10, // optional - max connections + idleTimeoutMillis: 30000 // optional - how long a client is allowed to remain idle before being closed + } + } +}); +``` Once the RapidQL instance is connected to the DB, you may query it. The object you're querying will have the following schema: - - DBType.DBName.Schema.Table.Operation +```javascript +DBType.DBName.Schema.Table.Operation +``` Where: @@ -121,36 +207,41 @@ For example, `PostgreSQL.Sample.public.users.select` will query the `Sample` Pos ### Select The most basic way to perform select queries is by passing equality comparisons: - - PostgreSQL.Sample.public.users.select(location: "US") +```javascript +PostgreSQL.Sample.public.users.select(location: "US") +``` This will find all users where location is 'US'. For more complex conditions use: - - PostgreSQL.Sample.public.users.select(birthyear: {"<=": "1997"}) +```javascript +PostgreSQL.Sample.public.users.select(birthyear: {"<=": "1997"}) +``` This will find users whose birth year is smaller than or equal to 1997. Using `.select(location:"US") is shorthand for .select(location:{"=":"US"})` You can have multiple conditions, mixing between comparison styles: - - PostgreSQL.Sample.public.users.select(location: 'US', birthyear: {"<=": "1997"}) +```javascript +PostgreSQL.Sample.public.users.select(location: 'US', birthyear: {"<=": "1997"}) +``` ### Complex queries (SKIP, LIMIT, ORDER BY) `PostgreSQL.Sample.public.users.select(location: "US")` is shorthand for `PostgreSQL.Sample.public.users.select(WHERE:{"location": "US"})`. Using the full syntax you may add skip, limit and order by clauses. +```javascript +PostgreSQL.Sample.public.users.select(WHERE:{"location": "US"}, LIMIT:"3", SKIP:"1", ORDERBY: {birthyear:"DESC"}) +``` - PostgreSQL.Sample.public.users.select(WHERE:{"location": "US"}, LIMIT:"3", SKIP:"1", ORDERBY: {birthyear:"DESC"}) - -Note case sensitivity. +***Note case sensitivity.*** ### Count Count works just like select, only it returns the `count` value. - - { - PostgreSQL.GCELogs.public.blockcalls.count(LIMIT:"10", GROUPBY:"package", ORDERBY:{count:"DESC"}) { - package, - count - } +```javascript +{ + PostgreSQL.GCELogs.public.blockcalls.count(LIMIT:"10", GROUPBY:"package", ORDERBY:{count:"DESC"}) { + package, + count } +} +``` ## Running in commandline Install RapidQL from NPM with the `-g` flag to use from command line. Than, you can use: @@ -160,4 +251,15 @@ Install RapidQL from NPM with the `-g` flag to use from command line. Than, you To run the query in `queryFile.rql`. RapidQL will also look for 2 optional hidden files: - `.rqlconfig` - json file containing your configurations (DB / RpaidAPI connection details). -- `.rqlcontext` - base context for RQL (define variables used in the query). \ No newline at end of file +- `.rqlcontext` - base context for RQL (define variables used in the query). + +## RQL Server +Install RapidQL from NPM with the `-g` flag to use from command line. +To use RQL from platforms other than nodejs, you can either use it as a command line executable (see above), or run it as a server. Running `rql-server` will set up an HTTP server, accepting RQL queries and returning their results in JSON format. + +### Parameters: + +- **-p** / **--port** : set the port rql will listen on. 3000 by default. +- **-c** / **--conf** : set the config file for rql to pull configurations from. By default - .rqlconfig at the same path. + +### API: diff --git a/bin/rql-query.js b/bin/rql-query.js new file mode 100644 index 0000000..5406b3f --- /dev/null +++ b/bin/rql-query.js @@ -0,0 +1,64 @@ +/** + * Created by iddo on 1/28/18. + */ +const program = require('commander'); +const { readFile, readJsonFile } = require('./utils'); +const RQL = require('./../index'); + +program + .option('--stdin-context', 'Stream context in stdin') + .option('--parse-context [format]', "Format of context", "json") + .option('--context-file [file]', "File to read context fromh. Defaults to .rqlcontext", ".rqlcontext") + .option('--config-file [file]', "File to read configurations from. Defaults to .rqlconfig", ".rqlconfig") + .option('-f, --query-file', "Argument is a file to read query from, rather than the query itself") + .parse(process.argv); + +async function getContext() { + let contextData; + if (!program.stdinContext) { + contextData = await readFile(program.contextFile); + } + + if (program.parseContext.toLowerCase() === "json") { + return JSON.parse(contextData); + } + + if (program.parseContext.toLowerCase() === "csv") { + const rqlClient = new RQL({}); + // feeding like a mad dog + return await rqlClient.query(`{ csv:Csv.read(file:csvFile){} }`, { + csvFile : program.contextFile + }); + } +} + +async function getConfig() { + return await readJsonFile(program.configFile); +} + +async function getQueryString() { + let query = program.args[0]; + + if (program.queryFile) { + return await readFile(query); + } else { + return query; + } +} + +async function execQuery() { + const context = await getContext(); + const config = await getConfig(); + let query = await getQueryString(); + + const rqlClient = new RQL(config); + + const result = await rqlClient.query(query, context); + + return JSON.stringify(result, null, 4); +} + +execQuery().then(console.log).catch(console.warn); + +// samples: +// rql query --context-file=data.csv --parse-context=csv --query-file q4.rql \ No newline at end of file diff --git a/bin/rql.js b/bin/rql.js index e6006ea..fc00ddc 100755 --- a/bin/rql.js +++ b/bin/rql.js @@ -11,47 +11,48 @@ const fs = require('fs'), let configs = {}, baseContext = {}; +const { readJsonFile } = require('./utils'); +const program = require('commander'); + + +program + .version('0.0.1') + .description("RapidQL Command Line Tool") + .command("query [query]", "perform and rql query") + .parse(process.argv); + + //Try and read configs -fs.readFile(CONFIG_FILE_NAME, 'utf8', function(err, data) { - if(!err && data) { - try { - configs = JSON.parse(data); - } catch (e) { - throw `Error reading config file: ${e}`; - } - } - //Try and base context - fs.readFile(CONTEXT_FILE_NAME, 'utf8', function(err, data) { - if(!err && data) { - try { - baseContext = JSON.parse(data); - } catch (e) { - throw `Error reading context file: ${e}`; - } - } - - //Read query - if (process.argv.length < 3) - throw `Please provide query file`; - const queryFile = process.argv[2]; - fs.readFile(queryFile, 'utf8', function (err, queryString) { - if (err) { - throw `Error reading query file: ${err}`; - } else if (!data) { - throw `Query file empty`; - } else { - //Perform query - const rqlClient = new RQL(configs); - rqlClient.query(queryString, baseContext) - .catch((err) => { - console.warn(`Error performing query: \n${e}`); - }) - .then((res) => { - console.log(JSON.stringify(res, null, 4)); - process.exit(0) - }); - } - }); - - }); -}); \ No newline at end of file +// fs.readFile(CONFIG_FILE_NAME, 'utf8', function(err, data) { +// if(!err && data) { +// try { +// configs = JSON.parse(data); +// } catch (e) { +// throw `Error reading config file: ${e}`; +// } +// } +// //Try and base context +// fs.readFile(CONTEXT_FILE_NAME, 'utf8', function(err, data) { +// if(!err && data) { +// try { +// baseContext = JSON.parse(data); +// } catch (e) { +// throw `Error reading context file: ${e}`; +// } +// } +// +// //Read query +// if (process.argv.length < 3) +// throw `Please provide query`; +// let queryString = process.argv[2]; +// const rqlClient = new RQL(configs); +// rqlClient.query(queryString, baseContext) +// .catch((err) => { +// console.warn(`Error performing query: \n${err}`); +// }) +// .then((res) => { +// console.log(JSON.stringify(res, null, 4)); +// process.exit(0) +// }); +// }); +// }); \ No newline at end of file diff --git a/bin/server.js b/bin/server.js new file mode 100755 index 0000000..14c110f --- /dev/null +++ b/bin/server.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node +/** + * Created by iddo on 7/20/17. + */ +console.log("server"); \ No newline at end of file diff --git a/bin/utils.js b/bin/utils.js new file mode 100644 index 0000000..20c6b2c --- /dev/null +++ b/bin/utils.js @@ -0,0 +1,43 @@ +/** + * Created by iddo on 1/28/18. + */ + +const fs = require('fs'); + +/** + * Reads and parses JSON in a file. Returns empty object if file empty + * @param filename + * @returns {Promise} + */ +function readJsonFile(filename) { + return new Promise((resolve, reject) => { + fs.readFile(filename, 'utf8', function(err, data) { + if (err) + return reject(`Error reading JSON file ${filename} : ${err}`); + else if (!data) + return resolve({}); + + try { + return resolve(JSON.parse(data)); + } catch (e) { + return reject(`Error parsing JSON in file ${filename} : ${e}`); + } + }); + }); +} +module.exports.readJsonFile = readJsonFile; + +function readFile(filename) { + return new Promise((resolve, reject) => { + fs.readFile(filename, 'utf8', function(err, data) { + if (err) + return reject(`Error reading file ${filename} : ${err}`); + else if (!data) + return reject(`File ${filename} is empty`); + else + return resolve(data); + }); + }); +} + +module.exports.readFile = readFile; \ No newline at end of file diff --git a/circle.yml b/circle.yml index 7f3f0a3..4c37daf 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,3 @@ machine: node: - version: 8.0.0 \ No newline at end of file + version: 9.0.0 \ No newline at end of file diff --git a/index.js b/index.js index 763e572..b00841d 100755 --- a/index.js +++ b/index.js @@ -6,12 +6,4 @@ const RQL = require('./src/RQL'); -module.exports = RQL; - -function pipe(...funcs) { - return function(values) { - return funcs.reduce(function(vals, f) { - return f(vals); - }, values); - } -} \ No newline at end of file +module.exports = RQL; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bbb72e2..cee4e30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,610 +1,2932 @@ { - "name": "@iddo/rapidql", - "version": "0.0.2", + "name": "rapidql", + "version": "0.0.6", "lockfileVersion": 1, + "requires": true, "dependencies": { - "ajv": { - "version": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=" + "@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==", + "requires": { + "@babel/highlight": "^7.8.3" + } }, - "ap": { - "version": "https://registry.npmjs.org/ap/-/ap-0.2.0.tgz", - "integrity": "sha1-rglCYAspkS8NKxTsYMRejzMLYRA=" + "@babel/core": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz", + "integrity": "sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.4", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.4", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3", + "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": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } }, - "asn1": { - "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "@babel/generator": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz", + "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==", + "requires": { + "@babel/types": "^7.8.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } }, - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + "@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==", + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } }, - "assertion-error": { - "version": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", - "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=" + "@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==", + "requires": { + "@babel/types": "^7.8.3" + } }, - "asynckit": { - "version": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "@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==", + "requires": { + "@babel/types": "^7.8.3" + } }, - "aws-sign2": { - "version": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } }, - "aws4": { - "version": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } }, - "balanced-match": { - "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" + "@babel/parser": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", + "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==" + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } }, - "bcrypt-pbkdf": { - "version": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true + "@babel/traverse": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz", + "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.4", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.4", + "@babel/types": "^7.8.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } }, - "bignumber.js": { - "version": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-3.1.2.tgz", - "integrity": "sha1-8725mtUmihX8HwvtL7AY4mk/4jY=" + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } }, - "boom": { - "version": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=" + "@istanbuljs/load-nyc-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", + "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==" + }, + "@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==" + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "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==", + "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==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "assertion-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", + "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "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==" }, "brace-expansion": { - "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=" + "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" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } }, "browser-stdout": { - "version": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "bson": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", + "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=" + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" + }, + "cacheman": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cacheman/-/cacheman-2.2.1.tgz", + "integrity": "sha1-NRDA3vEkKdYbeAEo/xj50oS7Arg=", + "requires": { + "cacheman-memory": "^1.0.2", + "ms": "^0.7.1" + } }, - "buffer-writer": { - "version": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz", - "integrity": "sha1-Iqk2kB4wKa/NdUfrRIfOtpejvwg=" + "cacheman-memory": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cacheman-memory/-/cacheman-memory-1.0.2.tgz", + "integrity": "sha1-OMebgPASsQO/qoNG1u10cwp+3pw=", + "requires": { + "lru-cache": "~2.7.x" + } + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } }, - "caseless": { - "version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "chai": { - "version": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", - "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.0.tgz", + "integrity": "sha1-MxoDkbVcOvh0CunDt0WLwcOAXm0=", + "requires": { + "assertion-error": "^1.0.1", + "check-error": "^1.0.1", + "deep-eql": "^2.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.0.0", + "type-detect": "^4.0.0" + } + }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "requires": { + "check-error": "^1.0.2" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" + }, + "chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" + } }, - "co": { - "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.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==" + }, + "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==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^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==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } }, - "colors": { - "version": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", - "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" + "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==", + "requires": { + "color-name": "1.1.3" + } }, - "combined-stream": { - "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=" + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "commander": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, "concat-map": { - "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "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==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, "core-util-is": { - "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "cryptiles": { - "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=" - }, - "dashdash": { - "version": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "dependencies": { - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } } } }, + "csv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/csv/-/csv-2.0.0.tgz", + "integrity": "sha1-DVHY+j7OJ4tUCfeVcX/PzVrOmz4=", + "requires": { + "csv-generate": "^2.0.0", + "csv-parse": "^2.0.0", + "csv-stringify": "^2.0.0", + "stream-transform": "^1.0.0" + } + }, + "csv-generate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-2.0.0.tgz", + "integrity": "sha1-q0/cMNtc5niwfVOITGn6VFYYslw=" + }, + "csv-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-2.0.0.tgz", + "integrity": "sha1-TqIslzIzmH8HaIxgGi1GAjT/VtE=" + }, + "csv-stringify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-2.0.1.tgz", + "integrity": "sha512-qoWgXJHmANfwIZFogezZjfN7KeaALrSe2zA5velb2tVEx0r7tgVuMfideB5gq12x5Z7LkvgOayXplOwlKcXnKQ==", + "requires": { + "lodash.get": "~4.4.2" + } + }, "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=" + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "deep-eql": { - "version": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-2.0.2.tgz", + "integrity": "sha1-sbrAblbwp2d3aG1Qyf63XC7XZ5o=", + "requires": { + "type-detect": "^3.0.0" + }, "dependencies": { "type-detect": { - "version": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-3.0.0.tgz", + "integrity": "sha1-RtDMhVOrt7E6NSsNbeov1Y8tm1U=" } } }, - "delayed-stream": { - "version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "requires": { + "strip-bom": "^4.0.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } }, "diff": { - "version": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", - "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=" + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + }, + "dnscache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dnscache/-/dnscache-1.0.1.tgz", + "integrity": "sha1-Qssrm/tej736OVqsdOEn/AUHTTE=", + "requires": { + "asap": "~2.0.3", + "lodash.clone": "~4.3.2" + } }, - "discontinuous-range": { - "version": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", - "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=" + "double-ended-queue": { + "version": "2.1.0-0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } }, - "ecc-jsbn": { - "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" + }, + "es6-promise": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" }, "escape-string-regexp": { - "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", - "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "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==", + "requires": { + "to-regex-range": "^5.0.1" + } }, - "extend": { - "version": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + "find-cache-dir": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz", + "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.0", + "pkg-dir": "^4.1.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==", + "requires": { + "locate-path": "^3.0.0" + } }, - "extsprintf": { - "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=" + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "requires": { + "is-buffer": "~2.0.3" + } }, - "forever-agent": { - "version": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } }, - "form-data": { - "version": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=" + "fromentries": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", + "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==" }, "fs.realpath": { - "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "generic-pool": { - "version": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.2.tgz", - "integrity": "sha1-iGvFvwvrfblugby7oHiBjeWmJoM=" + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "getpass": { - "version": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dependencies": { - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" + }, + "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==" + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" }, "glob": { - "version": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", - "integrity": "sha1-tCAqaQmbu00pKnwblbZoK2fr3JU=" + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "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" + } }, - "growl": { - "version": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", - "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "requires": { + "is-glob": "^4.0.1" + } }, - "har-schema": { - "version": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, - "har-validator": { - "version": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=" + "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==" + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } }, "has-flag": { - "version": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "hasha": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.1.0.tgz", + "integrity": "sha512-OFPDWmzPN1l7atOV1TgBVmNtBxaIysToK6Ve9DK+vT6pYuklw/nPNT+HJbZi0KDcI6vWB+9tgvZ5YD7fA3CXcA==", + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, - "hawk": { - "version": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=" + "html-escaper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", + "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==" }, - "hoek": { - "version": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, - "http-signature": { - "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=" + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" }, "inflight": { - "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" + "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": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "is-typedarray": { - "version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "isstream": { - "version": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jade": { - "version": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", - "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", - "dependencies": { - "commander": { - "version": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", - "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" - }, - "mkdirp": { - "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", - "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" - } + "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==", + "requires": { + "binary-extensions": "^2.0.0" } }, - "jodid25519": { - "version": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", - "optional": true - }, - "jsbn": { - "version": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "json-schema": { - "version": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "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=" + }, + "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==", + "requires": { + "is-extglob": "^2.1.1" + } }, - "json-stable-stringify": { - "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=" + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } }, - "json-stringify-safe": { - "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } }, - "json3": { - "version": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, - "jsonify": { - "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==" + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "requires": { + "append-transform": "^2.0.0" + } }, - "jsprim": { - "version": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, "dependencies": { - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, - "lodash._baseassign": { - "version": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=" + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + } }, - "lodash._basecopy": { - "version": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + } + } }, - "lodash._basecreate": { - "version": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", - "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=" + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } }, - "lodash._getnative": { - "version": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" + "istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A==", + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } }, - "lodash._isiterateecall": { - "version": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" + "js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } }, - "lodash.create": { - "version": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", - "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=" + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } }, - "lodash.isarguments": { - "version": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } }, - "lodash.isarray": { - "version": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "lodash._baseclone": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-4.5.7.tgz", + "integrity": "sha1-zkKt4IOE711i+nfDD2GkbmhvhDQ=" + }, + "lodash.clone": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.3.2.tgz", + "integrity": "sha1-5WsXa2gjp93jj38r9Y3n1ZcSAOk=", + "requires": { + "lodash._baseclone": "~4.5.0" + } }, - "lodash.keys": { - "version": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=" + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "requires": { + "chalk": "^2.0.1" + } }, "lru-cache": { - "version": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" }, - "mime-db": { - "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=" - }, - "mime-types": { - "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", - "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=" + "make-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", + "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } }, "minimatch": { - "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=" + "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": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mkdirp": { - "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=" + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } }, "mocha": { - "version": "https://registry.npmjs.org/mocha/-/mocha-3.2.0.tgz", - "integrity": "sha1-fcT0XlCIB1FxpoiWgU5q6et6heM=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.0.1.tgz", + "integrity": "sha512-9eWmWTdHLXh72rGrdZjNbG3aa1/3NRPpul1z0D979QpEnFdCG0Q5tv834N+94QEN2cysfV72YocQ3fn87s70fg==", + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "chokidar": "3.3.0", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "ms": "2.1.1", + "node-environment-flags": "1.0.6", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.0", + "yargs-parser": "13.1.1", + "yargs-unparser": "1.6.0" + }, "dependencies": { - "escape-string-regexp": { - "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "mocha-testcheck": { + "version": "1.0.0-rc.0", + "resolved": "https://registry.npmjs.org/mocha-testcheck/-/mocha-testcheck-1.0.0-rc.0.tgz", + "integrity": "sha1-BeUCAwQ74VN67yqH3ZbM1EdwJ3M=", + "requires": { + "testcheck": "^1.0.0-rc" + } + }, + "mongodb": { + "version": "2.2.30", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.30.tgz", + "integrity": "sha1-jM2AH2dsgXIEDC8rR+lgKg1WNKs=", + "requires": { + "es6-promise": "3.2.1", + "mongodb-core": "2.1.14", + "readable-stream": "2.2.7" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", + "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", + "requires": { + "buffer-shims": "~1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~1.0.0", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "~5.1.0" + } } } }, + "mongodb-core": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.14.tgz", + "integrity": "sha1-E8uidkImtb49GJkq8Mljzl6g8P0=", + "requires": { + "bson": "~1.0.4", + "require_optional": "~1.0.0" + } + }, "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", + "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8=" }, "mustache": { - "version": "https://registry.npmjs.org/mustache/-/mustache-2.3.0.tgz", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.0.tgz", "integrity": "sha1-QCj3d4sXcIpImTCm5SrDvKDaQdA=" }, "mysql": { - "version": "https://registry.npmjs.org/mysql/-/mysql-2.13.0.tgz", - "integrity": "sha1-mY8fjKRuLj3XFJzpgkE2U5hqrkc=" + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.14.1.tgz", + "integrity": "sha512-ZPXqQeYH7L1QPDyC77Rcp32cNCQnNjz8Y4BbF17tOjm5yhSfjFa3xS4PvuxWJtEEmwVc4ccI7sSntj4eyYRq0A==", + "requires": { + "bignumber.js": "4.0.2", + "readable-stream": "2.3.3", + "safe-buffer": "5.1.1", + "sqlstring": "2.2.0" + }, + "dependencies": { + "bignumber.js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.2.tgz", + "integrity": "sha1-LR3DfuWWiGfs6pC22k0W5oYI0h0=" + }, + "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=" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "sqlstring": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.2.0.tgz", + "integrity": "sha1-wxNcTqirzX5+50GklmqJHYak8ZE=" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } }, "nearley": { - "version": "https://registry.npmjs.org/nearley/-/nearley-2.7.10.tgz", - "integrity": "sha1-BvlTHgahcwJEY1rNfdAFSFVQpiM=" + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.11.0.tgz", + "integrity": "sha512-clqqhEuP0ZCJQ85Xv2I/4o2Gs/fvSR6fCg5ZHVE2c8evWyNk2G++ih4JOO3lMb/k/09x6ihQ2nzKUlB/APCWjg==", + "requires": { + "nomnom": "~1.6.2", + "railroad-diagrams": "^1.0.0", + "randexp": "^0.4.2" + }, + "dependencies": { + "colors": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" + }, + "discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=" + }, + "nomnom": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", + "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", + "requires": { + "colors": "0.5.x", + "underscore": "~1.4.4" + } + }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=" + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" + } + } }, - "nomnom": { - "version": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", - "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=" + "node-environment-flags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } }, - "oauth-sign": { - "version": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "requires": { + "process-on-spawn": "^1.0.0" + } }, - "object-assign": { - "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", - "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=" + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "nyc": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.0.tgz", + "integrity": "sha512-qcLBlNCKMDVuKb7d1fpxjPR8sHeMVX0CHarXAVzrVWoFrigCkYR8xcrjfXSPi5HXM7EU78L6ywO7w1c5rZNCNg==", + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", + "js-yaml": "^3.13.1", + "make-dir": "^3.0.0", + "node-preload": "^0.2.0", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "uuid": "^3.3.3", + "yargs": "^15.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "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==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.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==", + "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==" + }, + "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==" + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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" + } + }, + "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==" + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + }, + "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==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz", + "integrity": "sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==", + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.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": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^16.1.0" + } + }, + "yargs-parser": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz", + "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } }, - "once": { - "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + "object-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.2.0.tgz", + "integrity": "sha512-smRWXzkvxw72VquyZ0wggySl7PFUtoDhvhpdwgESXxUrH7vVhhp9asfup1+rVLrhsl7L45Ee1Q/l5R2Ul4MwUg==" + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } }, - "packet-reader": { - "version": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.2.0.tgz", - "integrity": "sha1-gZ300BC4LV6lZx+KGjrPA5vNdwA=" + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } }, - "path-is-absolute": { - "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } }, - "pegjs": { - "version": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", - "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=" + "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==", + "requires": { + "p-try": "^2.0.0" + } }, - "performance-now": { - "version": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + "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==", + "requires": { + "p-limit": "^2.0.0" + } }, - "pg": { - "version": "https://registry.npmjs.org/pg/-/pg-6.1.2.tgz", - "integrity": "sha1-LIlqdDRQLiuTjBAPwIW06XShhts=" + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "requires": { + "aggregate-error": "^3.0.0" + } }, - "pg-connection-string": { - "version": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", - "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=" + "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==" + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } }, - "pg-pool": { - "version": "https://registry.npmjs.org/pg-pool/-/pg-pool-1.6.0.tgz", - "integrity": "sha1-LjABmZJ7bX22vnHi40Nd3d3we0E=" + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" }, - "pg-types": { - "version": "https://registry.npmjs.org/pg-types/-/pg-types-1.11.0.tgz", - "integrity": "sha1-qukagtlStjO7iNAGNQoWbar26pA=" + "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=" }, - "pgpass": { - "version": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.1.tgz", - "integrity": "sha1-Dei1vvmTKV2Qp+F9l29Wjc0l1J8=" + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, - "postgres-array": { - "version": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.2.tgz", - "integrity": "sha1-jgsy6wO/d6XAp4UeBEHBaaJWojg=" + "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==" }, - "postgres-bytea": { - "version": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" }, - "postgres-date": { - "version": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz", - "integrity": "sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g=" + "pegjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=" }, - "postgres-interval": { - "version": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.0.2.tgz", - "integrity": "sha1-cmFDjYYrQSkhxv23YXZoQktzpu0=" + "pg": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/pg/-/pg-6.4.2.tgz", + "integrity": "sha1-w2QBEGDqx6UHoq4GPrhX7OkQ4n8=", + "requires": { + "buffer-writer": "1.0.1", + "js-string-escape": "1.0.1", + "packet-reader": "0.3.1", + "pg-connection-string": "0.1.3", + "pg-pool": "1.*", + "pg-types": "1.*", + "pgpass": "1.*", + "semver": "4.3.2" + }, + "dependencies": { + "buffer-writer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz", + "integrity": "sha1-Iqk2kB4wKa/NdUfrRIfOtpejvwg=" + }, + "generic-pool": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.3.tgz", + "integrity": "sha1-eAw29p360FpaBF3Te+etyhGk9v8=" + }, + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=" + }, + "packet-reader": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz", + "integrity": "sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc=" + }, + "pg-connection-string": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", + "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=" + }, + "pg-pool": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-1.8.0.tgz", + "integrity": "sha1-9+xzgkw3oD8Hb1G/33DjQBR8Tzc=", + "requires": { + "generic-pool": "2.4.3", + "object-assign": "4.1.0" + } + }, + "pg-types": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.12.1.tgz", + "integrity": "sha1-1kCH45A7WP+q0nnnWVxSIIoUw9I=", + "requires": { + "postgres-array": "~1.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.0", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", + "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", + "requires": { + "split": "^1.0.0" + } + }, + "postgres-array": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.2.tgz", + "integrity": "sha1-jgsy6wO/d6XAp4UeBEHBaaJWojg=" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + }, + "postgres-date": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz", + "integrity": "sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g=" + }, + "postgres-interval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.1.tgz", + "integrity": "sha512-OkuCi9t/3CZmeQreutGgx/OVNv9MKHGIT5jH8KldQ4NLYXkvmT9nDVxEuCENlNwhlGPE374oA/xMqn05G49pHA==", + "requires": { + "xtend": "^4.0.0" + } + }, + "semver": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", + "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "requires": { + "through": "2" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } }, - "punycode": { - "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "picomatch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==" + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + } + } }, - "qs": { - "version": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "requires": { + "fromentries": "^1.2.0" + } }, "query-string": { - "version": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=" - }, - "railroad-diagrams": { - "version": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", - "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=" - }, - "randexp": { - "version": "https://registry.npmjs.org/randexp/-/randexp-0.4.4.tgz", - "integrity": "sha1-umgmX0qfnoX1gU004WApH5OfFo4=" + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + } + } }, "rapidapi-connect": { - "version": "https://registry.npmjs.org/rapidapi-connect/-/rapidapi-connect-0.0.4.tgz", + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/rapidapi-connect/-/rapidapi-connect-0.0.4.tgz", "integrity": "sha1-FpQS9iZNhi6MhMf98g9MT+uU4tk=", + "requires": { + "chai": "^3.5.0", + "mocha": "^2.5.3", + "request": "^2.72.0", + "ws": "^2.1.0" + }, "dependencies": { + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "requires": { + "assertion-error": "^1.0.1", + "deep-eql": "^0.1.3", + "type-detect": "^1.0.0" + } + }, "commander": { - "version": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=" }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "requires": { + "type-detect": "0.1.1" + }, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" + } + } + }, + "diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=" + }, + "escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=" + }, "glob": { - "version": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", - "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=" + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" + } + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" }, "minimatch": { - "version": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", - "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "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" + } }, "mocha": { - "version": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", - "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", + "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", + "requires": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.11", + "growl": "1.9.2", + "jade": "0.26.3", + "mkdirp": "0.5.1", + "supports-color": "1.2.0", + "to-iso-string": "0.0.2" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" }, "supports-color": { - "version": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=" + }, + "to-iso-string": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", + "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=" + }, + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=" + }, + "ultron": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz", + "integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ=" + }, + "ws": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz", + "integrity": "sha1-a5Sz5EfLajY/eF6vlK9jWejoHIA=", + "requires": { + "safe-buffer": "~5.0.1", + "ultron": "~1.1.0" + } } } }, - "readable-stream": { - "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=" + "readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "requires": { + "picomatch": "^2.0.4" + } + }, + "redis": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.7.1.tgz", + "integrity": "sha1-fVb3h1uYsgQQtxU58dh47Vjr9Go=", + "requires": { + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.5.0" + } + }, + "redis-commands": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz", + "integrity": "sha1-gdgm9F+pyLIBH0zXoP5ZfSQdRCs=" + }, + "redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "requires": { + "es6-error": "^4.0.1" + } }, "request": { - "version": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=" + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.x.x" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "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=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.x.x" + } + }, + "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" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "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.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" + } + }, + "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" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "requires": { + "ajv": "^4.9.1", + "har-schema": "^1.0.5" + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "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=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "~0.0.0" + } + }, + "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=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "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" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "~1.30.0" + } + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.x.x" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "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", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "requires": { + "punycode": "^1.4.1" + } + }, + "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=", + "optional": true + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "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" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "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==" + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + } + } }, - "ret": { - "version": "https://registry.npmjs.org/ret/-/ret-0.1.13.tgz", - "integrity": "sha1-OMJwLs5lSXiUHt2LffrGru70Bn0=" + "resolve": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", + "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "rimraf": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.1.tgz", + "integrity": "sha512-IQ4ikL8SjBiEDZfk+DFVwqRK8md24RWMEJkdSlgNLkyyAImcjf8SWvU1qFMDOb4igBClbTQ/ugPqXcRwdFTxZw==", + "requires": { + "glob": "^7.1.3" + } }, "safe-buffer": { - "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "semver": { - "version": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", - "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, - "sigmund": { - "version": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "simple-rate-limiter": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/simple-rate-limiter/-/simple-rate-limiter-0.2.3.tgz", "integrity": "sha1-MGRm+v8X4fIu51iwo3zUU+g1G0E=" }, - "sntp": { - "version": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=" - }, - "split": { - "version": "https://registry.npmjs.org/split/-/split-1.0.0.tgz", - "integrity": "sha1-xDlc5oOrzSVLwo/h2rtuXCfc/64=" - }, - "sqlstring": { - "version": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.2.0.tgz", - "integrity": "sha1-wxNcTqirzX5+50GklmqJHYak8ZE=" - }, - "sshpk": { - "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", - "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, "dependencies": { - "assert-plus": { - "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } } } }, - "strict-uri-encode": { - "version": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "stream-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-1.0.0.tgz", + "integrity": "sha1-1O84aRPW7BAgUrx1/AUWlHqWHxA=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } }, - "string_decoder": { - "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "string.prototype.trimleft": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } }, - "stringstream": { - "version": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } }, - "supports-color": { - "version": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", - "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=" + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } }, - "through": { - "version": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" }, - "to-iso-string": { - "version": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", - "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=" + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, - "tough-cookie": { - "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=" + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "requires": { + "has-flag": "^3.0.0" + } }, - "tunnel-agent": { - "version": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=" + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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" + } + } + } }, - "tweetnacl": { - "version": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true + "testcheck": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/testcheck/-/testcheck-1.0.0-rc.2.tgz", + "integrity": "sha1-ETVqJbhFde/gsIV0UehbX6dO5OQ=" + }, + "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=" + }, + "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==", + "requires": { + "is-number": "^7.0.0" + } }, "type-detect": { - "version": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", - "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=" - }, - "ultron": { - "version": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz", - "integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ=" + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", + "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=" + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + }, + "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==", + "requires": { + "is-typedarray": "^1.0.0" + } }, - "underscore": { - "version": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", - "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { - "version": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "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" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "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" + } }, - "verror": { - "version": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=" + "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==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "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==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^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==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } }, "wrappy": { - "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "ws": { - "version": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz", - "integrity": "sha1-a5Sz5EfLajY/eF6vlK9jWejoHIA=" + "write-file-atomic": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", + "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", + "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==" + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "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.1" + }, + "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==" + }, + "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==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^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==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } }, - "xtend": { - "version": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + "yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + } } } } diff --git a/package.json b/package.json index 152438c..9a79cd4 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,44 @@ { - "name": "@iddo/rapidql", - "version": "0.0.2", + "name": "rapidql", + "version": "0.0.6", "description": "", "main": "index.js", "scripts": { - "test": "./node_modules/mocha/bin/mocha" + "test": "./node_modules/mocha/bin/mocha --recursive", + "run": "nyc mocha --recursive" }, "engines": { "node": "6.1.0" }, "bin": { - "rql": "bin/rql.js" + "rql": "bin/rql.js", + "rql-server": "bin/server.js" }, "keywords": [], "author": "", "license": "UNLICENSED", "dependencies": { + "cacheman": "^2.2.1", + "chai": "^4.1.0", + "chai-as-promised": "^7.1.1", "commander": "^2.11.0", - "mocha": "^3.2.0", - "mustache": "^2.3.0", + "csv": "^2.0.0", + "dnscache": "^1.0.1", + "mocha": "^7.0.0", + "mocha-testcheck": "^1.0.0-rc.0", + "mongodb": "^2.2.30", + "mustache": "^2.2.1", "mysql": "^2.13.0", "nearley": "^2.7.10", + "nyc": "^15.0.0", + "object-hash": "^1.2.0", "pegjs": "^0.10.0", "pg": "^6.1.2", "query-string": "^4.3.4", "rapidapi-connect": "0.0.4", + "redis": "^2.7.1", "request": "^2.81.0", - "simple-rate-limiter": "^0.2.3" + "simple-rate-limiter": "^0.2.3", + "testcheck": "^1.0.0-rc.2" } } diff --git a/src/Nodes/ArrayNode.js b/src/Nodes/ArrayNode.js index 6923279..a461f8d 100644 --- a/src/Nodes/ArrayNode.js +++ b/src/Nodes/ArrayNode.js @@ -4,7 +4,7 @@ "use strict"; const ObjectNode = require('./ObjectNode'); const LeafNode = require('./LeafNode'); - +const { createMixedContext } = require('./utils'); class ArrayNode { constructor(name, children) { @@ -21,22 +21,37 @@ class ArrayNode { let arr = context[this.getName()]; let promises = []; if (!Array.isArray(arr)) { - return new Promise((resolve, reject) => {reject(`TypeError: element ${this.getName()} in context is not an array`)}); + return Promise.reject(`TypeError: element ${this.getName()} in context is not an array`); } else { for (let i in arr) { + /** + * If iterating over array of objects, inner context is the object in the array. + * If iterating over array of scalars ([s]), inner context is the object {[this.name]:s} + */ let obj = arr[i]; - let innerContext = Object.assign({}, context); - innerContext[this.getName()] = obj; + let innerContext; let innerNode; - if(typeof obj === "object") + if(typeof obj === "object") { + innerContext = createMixedContext(context, { + [this.getName()] : obj + }); innerNode = new ObjectNode(this.getName(), this.children); - else if (this.children.length > 0) + } else if (this.children.length > 0) { + innerContext = createMixedContext(context, { + [this.getName()] : { + [this.getName()] : obj + } + }); innerNode = new ObjectNode(this.getName(), this.children); - else + } else { + innerContext = createMixedContext(context, { + [this.getName()] : obj + }); innerNode = new LeafNode(this.getName()); + } promises.push(innerNode.eval(innerContext, ops)); } diff --git a/src/Nodes/CachedFunctionNode.js b/src/Nodes/CachedFunctionNode.js new file mode 100644 index 0000000..204d4e0 --- /dev/null +++ b/src/Nodes/CachedFunctionNode.js @@ -0,0 +1,41 @@ +/** + * Created by iddo on 1/21/18. + */ +"use strict"; + +const hash = require('object-hash'); + +if (!global._function_node_cache) global._function_node_cache = {}; + +class CachedFunctionNode { + constructor(innerNode) { + this.innerNode = innerNode; + } + + getName() { + return this.innerNode.getName(); + } + + //noinspection JSAnnotator + async eval(context, ops) { + + const processedArgs = this.innerNode.getProcessedArgs(context); + + const innerNodeHash = hash({ + name: this.innerNode.getName(), + args: processedArgs + }); + + if (innerNodeHash in global._function_node_cache) { + ops.logger.log(`Cache hit: ${this.innerNode.getName()}`); + return await this.innerNode.continueTree(context, ops, await global._function_node_cache[innerNodeHash]); + } else { + ops.logger.log(`Cache miss: ${this.innerNode.getName()}`); + global._function_node_cache[innerNodeHash] = this.innerNode.performFunction(processedArgs, context, ops); + const innerNodeValue = await global._function_node_cache[innerNodeHash]; + return await this.innerNode.continueTree(context, ops, innerNodeValue); + } + } +} + +module.exports = CachedFunctionNode; \ No newline at end of file diff --git a/src/Nodes/CompositeNode.js b/src/Nodes/CompositeNode.js index 8ecfff1..83a29f1 100644 --- a/src/Nodes/CompositeNode.js +++ b/src/Nodes/CompositeNode.js @@ -17,7 +17,23 @@ class CompositeNode { //noinspection JSAnnotator eval(context, ops) { - if(Array.isArray(context[this.getName()])) { + if (!context[this.getName()]) { + // If object not in context, return error + return new Promise((resolve, reject) => { + reject(`Key "${this.getName()}" does not exist in context`); + }); + } else if (typeof context[this.getName()] !== 'object') { + // If object not an object, return error + return new Promise((resolve, reject) => { + reject(`Cannot expand key "${this.getName()}" of type ${typeof context[this.getName()]}`); + }); + } else if (this.children.length === 0) { + // If object has no children, append all + return new Promise((resolve, reject) => { + resolve(context[this.getName()]); + }); + } + else if(Array.isArray(context[this.getName()])) { return (new ArrayNode(this.getName(), this.children)).eval(context, ops); } else { return (new ObjectNode(this.getName(), this.children)).eval(context, ops); diff --git a/src/Nodes/FlatObjectNode.js b/src/Nodes/FlatObjectNode.js new file mode 100644 index 0000000..ca70279 --- /dev/null +++ b/src/Nodes/FlatObjectNode.js @@ -0,0 +1,22 @@ +/** + * Created by iddo on 4/19/18. + */ +"use strict"; +const utils = require('./utils'); + +class FlatObjectNode { + constructor(innerNode) { + this.innerNode = innerNode; + } + + getName() { + return this.innerNode.getName(); + } + + //noinspection JSAnnotator + async eval(context, ops) { + return await this.innerNode.eval(context, ops); + } +} + +module.exports = FlatObjectNode; \ No newline at end of file diff --git a/src/Nodes/FunctionNode.js b/src/Nodes/FunctionNode.js index 2588573..de6aa40 100644 --- a/src/Nodes/FunctionNode.js +++ b/src/Nodes/FunctionNode.js @@ -5,7 +5,10 @@ const LeafNode = require('./LeafNode'), ObjectNode = require('./ObjectNode'), ArrayNode = require('./ArrayNode'), - CompositeNode = require('./CompositeNode'); + CompositeNode = require('./CompositeNode'), + LogicNode = require('./LogicNode'); + +const { createMixedContext, resolve } = require('./utils'); const Mustache = require('mustache'); @@ -13,24 +16,17 @@ const supportedTypes = { "RapidAPI" : require('./FunctionNodes/RapidAPIDriver/RapidAPINode'), "PostgreSQL" : require('./FunctionNodes/PostgreSQLDriver/PostgreSQLNode'), "MySQL" : require('./FunctionNodes/MySQLDriver/MySQLNode'), - "Http" : require('./FunctionNodes/HttpDriver/HttpNode') + "Http" : require('./FunctionNodes/HttpDriver/HttpNode'), + "Redis" : require('./FunctionNodes/RedisDriver/RedisNode'), + "MongoDB" : require('./FunctionNodes/MongoDBDriver/MongoDBNode'), + "MapReduce" : require('./FunctionNodes/MapReduce/MapReduceNode'), + "Csv" : require('./FunctionNodes/CsvDriver/CsvNode'), + "Logic" : require('./LogicNode'), + ...LogicNode.logicFunctions // This is a work around for the parser, as it first initializes logic nodes as function nodes (if(...){...}) and then converts them to logic nodes and it sees prefixing '@'. Better solution TBD }; const SEP_CHAR = '.'; -/** - * Get's a key's value from the context or throws a 'Does not exist' error - * @param key - * @param context - */ -function getFromContext(key, context) { - if (context.hasOwnProperty(key)) - return context[key]; - else { - throw `Name error: name ${key} does not exist in context`; - } -} - /** * Check if a string is wrapped in quotes * @param arg the string to be checked @@ -66,7 +62,7 @@ function replaceVariables(value, context) { * @returns {{}} */ function recursiveReplace(args, context) { - let processedArgs = {}; + let processedArgs = Array.isArray(args) ? [] : {}; for (let key in args) { if (args.hasOwnProperty(key)) { let arg = args[key]; @@ -78,10 +74,13 @@ function recursiveReplace(args, context) { switch (typeof arg) { case 'string': //Check for quotes: - processedArgs[key] = quoted(arg) ? replaceVariables(arg.slice(1, -1), context) : getFromContext(arg, context); + processedArgs[key] = quoted(arg) ? replaceVariables(arg.slice(1, -1), context) : resolve(arg, context); //If literal - Remove quotes, render template and add to processed args // If Variable - get from context break; + case 'number': + processedArgs[key] = arg; + break; case 'object': processedArgs[key] = recursiveReplace(arg, context); break; @@ -115,6 +114,18 @@ class FunctionNode { } } + /** + * Process arguments and replace variables based on context + * @param context + * @returns {{}} + */ + getProcessedArgs(context) { + //Process args in context + //If they have " " -> string literal (remove quotes) + //If they don't -> fetch from context + return recursiveReplace(this.args, context); + } + /** * * @returns {string|*} @@ -123,49 +134,77 @@ class FunctionNode { return `${this.type}.${this.name}`; } + //noinspection JSAnnotator - eval(context, ops) { - return new Promise((resolve, reject) => { - //Process args in context - //If they have " " -> string literal (remove quotes) - //If they don't -> fetch from context + /** + * + * @param context + * @param ops + * @param cachedResult (optional) if set, will not perform query, but use that result + * @returns {Promise} + */ + async eval(context, ops) { try { - const processedArgs = recursiveReplace(this.args, context); + const processedArgs = this.getProcessedArgs(context); - //Determine material node type and materialize itself - const MaterialClass = supportedTypes[this.type]; - const materialNode = new MaterialClass(this.getName(), this.children, processedArgs); + const materialValuePayload = await this.performFunction(processedArgs, context, ops); + return await this.continueTree(context, ops, materialValuePayload); - materialNode.eval(context, ops).catch(reject).then((payload) => { - //Create context and add payload to it - let ctx = Object.assign({}, context); - ctx[this.getName()] = payload; + } catch (e) { + throw `Error in ${this.getName()}: ${e}`; + } + + } - //Process down the tree... - if(typeof payload == 'string'){ - (new LeafNode(this.getName())).eval(ctx).then(resolve).catch(reject); - } else if(typeof payload == 'object') { - let innerContext = Object.assign({}, context); - innerContext[this.getName()] = payload; + async performFunction(processedArgs, context, ops) { - let innerNode = new CompositeNode(this.getName(), this.children); + const exStart = process.hrtime(); - innerNode.eval(innerContext, ops).then(resolve).catch(reject); + //Determine material node type and materialize itself + const MaterialClass = supportedTypes[this.type]; + const materialNode = new MaterialClass(this.getName(), this.children, processedArgs); + const val = await materialNode.eval(context, ops); - } else { //"You don't het another chance, life ain't a Nintendo game" - Eminem - reject(`APIError: got invalid data type ${typeof payload} which is not supported by function nodes`); - } - }); - } catch (e) { - reject(`Error parsing arguments: ${e}`); - } - }); - + const exEnd = process.hrtime(exStart); + ops.logger.log(`Executing: ${materialNode.signature || materialNode.getName()} (took ${exEnd[0]}s ${exEnd[1]/1000000}ms)`); + + return val; + } + + /** + * Appends payload from function node to context and continues tree execution + * @param materialNode + * @param context + * @param ops + * @param payload + * @returns {Promise.<*>} + */ + async continueTree(context, ops, payload) { + //Create context and add payload to it + let ctx = Object.assign({}, context); //TODO optimize to use mixedContext wrapper + ctx[this.getName()] = payload; + + //Process down the tree... + if(typeof payload === 'string') { + return await (new LeafNode(this.getName())).eval(ctx); + } else if(typeof payload === 'object') { + let innerContext = createMixedContext(context, { + [this.getName()] : payload + }); + // let innerContext = Object.assign({}, context); + // innerContext[this.getName()] = payload; + + let innerNode = new CompositeNode(this.getName(), this.children); + + return await innerNode.eval(innerContext, ops); + + } else { //"You don't het another chance, life ain't a Nintendo game" - Eminem + throw `APIError: got invalid data type ${typeof payload} which is not supported by function nodes` ; + } } } FunctionNode.recursiveReplace = recursiveReplace; -FunctionNode.getFromContext = getFromContext; FunctionNode.quoted = quoted; FunctionNode.removeQuotes = removeQuotes; diff --git a/src/Nodes/FunctionNodes/CsvDriver/CsvNode.js b/src/Nodes/FunctionNodes/CsvDriver/CsvNode.js new file mode 100644 index 0000000..0e31aff --- /dev/null +++ b/src/Nodes/FunctionNodes/CsvDriver/CsvNode.js @@ -0,0 +1,48 @@ +/** + * Created by iddo on 1/5/18. + */ +const fs = require('fs'); +const parse = require('csv').parse; + +class CsvNode { + constructor(name, children, args) { + this.name = name; + this.args = args; + this.children = children; + } + + getName() { + return `${this.name}`; + } + + eval(context, ops) { + return new Promise((resolve, reject) => { + const self = this; + + const file = self.args['file'] || "", + auto_parse = self.args['auto_parse'] !== undefined ? self.args['auto_parse'] === 'true' : true, + auto_parse_date = self.args['auto_parse_date'] !== undefined ? self.args['auto_parse_date'] === 'true' : true, + columns = self.args['columns'] !== undefined ? self.args['columns'] === 'true' : true, + delimiter = self.args['delimiter'] || ","; + + const parser = parse({columns, delimiter, auto_parse, auto_parse_date}, function(err, data){ + if (err) + reject(`Error reading CSV file: ${err}`); + resolve(data); + }); + + fs.createReadStream(file).pipe(parser); + + }); + } +} +module.exports = CsvNode; + + +/** TEST **/ +// let node = new CsvNode('Csv.read', [], { +// file: "./sample.csv", +// columns: "true" +// }); +// +// node.eval({}, {}).then(console.log).catch(console.warn); \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/CsvDriver/test/csv.js b/src/Nodes/FunctionNodes/CsvDriver/test/csv.js new file mode 100644 index 0000000..ebfd09b --- /dev/null +++ b/src/Nodes/FunctionNodes/CsvDriver/test/csv.js @@ -0,0 +1,22 @@ +/** + * Created by iddo on 1/5/18. + */ +const { expect, assert } = require('chai'); +const CsvNode = require('./../CsvNode'); + +module.exports = () => { + describe('reading csv file', () => { + it('should read a file with a header column', async () => { + let node = new CsvNode('Csv.read', [], { + file: "./src/Nodes/FunctionNodes/CsvDriver/test/sample.csv", + columns: "true" + }); + + const results = await node.eval({}, {}); + + assert.equal(results.length, 5); // 5 results + results.forEach(r => assert.typeOf(r, 'object')); // all objects + results.forEach(r => assert.hasAllKeys(r, ['name','age','sex'])); // has right keys + }); + }); +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/CsvDriver/test/sample.csv b/src/Nodes/FunctionNodes/CsvDriver/test/sample.csv new file mode 100644 index 0000000..c1130a6 --- /dev/null +++ b/src/Nodes/FunctionNodes/CsvDriver/test/sample.csv @@ -0,0 +1,6 @@ +name,age,sex +iddo,20,m +matan,13,m +daria,18,f +sharon,45,f +michael,51,m \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/HttpDriver/HttpNode.js b/src/Nodes/FunctionNodes/HttpDriver/HttpNode.js index a8cb860..bb01b97 100644 --- a/src/Nodes/FunctionNodes/HttpDriver/HttpNode.js +++ b/src/Nodes/FunctionNodes/HttpDriver/HttpNode.js @@ -10,6 +10,16 @@ const _request = require('request'), queryString = require("query-string"); const limit = require("simple-rate-limiter"); +const dns = require('dns'), + dnscache = require('dnscache')({ + "enable" : true, + "ttl" : 360000, + "cachesize" : 1000 + }); + + + + global.request = null; function getRequestClient(ops) { if (global.request !== null) @@ -32,7 +42,8 @@ function getRequestClient(ops) { const functions = { get: null, post: null, - put: null + put: null, + delete: null }; class HttpNode { @@ -46,29 +57,66 @@ class HttpNode { return `${this.name}`; } + get signature() { + return `Http.${this.operation.toUpperCase()} - ${this.urlWithParameters}`; + } + + get queryParameters() { + return this.args['params'] || {} + } + + get urlWithParameters() { + return `${(this.args['url'] || "")}${Object.keys(this.queryParameters).length ? "?" : ""}${queryString.stringify(this.queryParameters)}`; + } + + get tokenizedName() { + return this.name.split(SEP_CHAR); + } + + get operation() { + return this.tokenizedName[1]; + } + eval(context, ops) { + const self = this; return new Promise((resolve, reject) => { - const tokenizedName = this.name.split(SEP_CHAR); - const operation = tokenizedName[1]; + const tokenizedName = this.tokenizedName; + const operation = this.operation; + + // ops.Http is default HTTP parameters + // self.args is patameters at time of call + if(ops.Http === undefined) + ops.Http = {}; + if(!ops.Http.headers) + ops.Http.headers = {}; + + + if(self.args === undefined) + self.args = {}; if(!functions.hasOwnProperty(operation)) return reject(`Operation Error: operation ${operation} does not exist / is not supported`); - const params = self.args['params'] || {}, - url = `${(self.args['url'] || "")}?${queryString.stringify(params)}`, - body = (operation === 'get') ? (null) : (self.args['body'] || {}), - form = (operation === 'get') ? (null) : (self.args['form'] || {}), + const params = self.queryParameters, + url = self.urlWithParameters, + body = (operation === 'get') ? (null) : (self.args['body'] || ""), + form = (operation === 'get') ? (null) : (self.args['form'] || null), json = (operation === 'get') ? (null) : (self.args['json'] || null), - headers = self.args['headers'] || {}, - bearer = self.args['bearer'] || null; - + headers = { ...ops.Http['headers'], ...self.args['headers'] } || {}, + bearer = (self.args['bearer']) ? self.args['bearer'] : ops.Http['bearer'] || null, + basic = (self.args['basic']) ? self.args['basic'] : ops.Http['basic'] || null, + stopOnError = self.args.hasOwnProperty('stopOnError') ? self.args['stopOnError'] : true; if (bearer !== null) { headers['Authorization'] = `Bearer ${bearer}`; } + if (basic !== null) { + headers['Authorization'] = `Basic ${new Buffer(basic['username'] + ":" + basic['password']).toString("base64")}`; + } + getRequestClient(ops)(url, { method : operation, headers : headers, @@ -82,10 +130,10 @@ class HttpNode { if(!response) return reject(`HttpError: no response from ${url}`); - if(response.statusCode > 299) - return reject(`HttpError: got non-2xx response from ${url}: \ncode: ${response.statusCode}, \ncontent: ${response}`); + if(response.statusCode > 299 && stopOnError) + return reject(`HttpError: got non-2xx response from ${url}: \ncode: ${response.statusCode}, \ncontent: ${response}, \nmessage: ${body}`); - if(typeof body != OBJECT_TYPE) { + if(typeof body !== OBJECT_TYPE) { try { return resolve(JSON.parse(body)); } catch (e) { diff --git a/src/Nodes/FunctionNodes/HttpDriver/test/http.js b/src/Nodes/FunctionNodes/HttpDriver/test/http.js new file mode 100644 index 0000000..61dc534 --- /dev/null +++ b/src/Nodes/FunctionNodes/HttpDriver/test/http.js @@ -0,0 +1,69 @@ +/** + * Created by iddo on 7/27/17. + */ +const { expect, assert } = require('chai'); +const HttpNode = require('./../HttpNode'); + +module.exports = () => { + describe('get request', () => { + it('should perform simple get request', async () => { + let n = new HttpNode('Http.get', [], { + url: "https://httpbin.org/get", + params: { + a: "b", + c: 1 + } + }); + + let res = await n.eval({}, {}); + assert.equal(res.args.a, "b"); + assert.equal(res.args.c, 1); + }).timeout(10000); + + it("should properly send headers", async () => { + let n = new HttpNode('Http.get', [], { + url: "https://httpbin.org/get", + headers: { + "X-Test": "b" + } + }); + + let res = await n.eval({}, {}); + assert.equal(res.headers["X-Test"], "b"); + assert.equal(res.headers["Host"], "httpbin.org"); + }).timeout(10000); + }); + + describe('post request', () => { + it('should send form data', async () => { + let n = new HttpNode('Http.post', [], { + url: "https://httpbin.org/post", + form: { + a: "b", + c: 1 + } + }); + + let res = await n.eval({}, {}); + assert.equal(res.form.a, "b"); + assert.equal(res.form.c, 1); + assert.equal(res.json, null); + assert.equal(res.url, "https://httpbin.org/post"); + }).timeout(10000); + + it('should send json data', async () => { + let n = new HttpNode('Http.post', [], { + url: "https://httpbin.org/post", + json: { + a: "b", + c: 1 + } + }); + + let res = await n.eval({}, {}); + assert.equal(res.json.a, "b"); + assert.equal(res.json.c, 1); + assert.equal(res.url, "https://httpbin.org/post"); + }).timeout(10000); + }); +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MapReduce/MapReduceNode.js b/src/Nodes/FunctionNodes/MapReduce/MapReduceNode.js new file mode 100644 index 0000000..a2c6425 --- /dev/null +++ b/src/Nodes/FunctionNodes/MapReduce/MapReduceNode.js @@ -0,0 +1,65 @@ +/** + * Created by iddo on 8/27/17. + */ +const SEP_CHAR = '.'; +const NODE_NAME = 'MapReduce'; +const mapReduce = require('./mapReduce').mapReduce; + +class MapReduceNode { + constructor (name, children, args) { + // Check # of children + if (0 >= children.length) { + throw `MapReduce Error: node needs 1 child. 0 found.`; + } else if (1 < children.length) { + console.warn(`MapReduce Warning: MapReduce node got ${children.length} children. Only first one - ${children[0].getName()} will be used. Children: `); + children.map((child) => {console.log (`- ${child.getName()}`)}); + } + + // Get pipe name + const tokenizedName = name.split(SEP_CHAR); + // Check there is a pipe object identifier + if (tokenizedName.length < 2) + throw `MapReduce Error: No pipe name. Should be MapReduce.pipeName(){}`; + const pipeName = tokenizedName[1]; + + this.name = name; + this.args = args; + this.children = children; + this.pipeName = pipeName; + } + + getName () { + return `${this.name}`; + } + + async eval (context, ops) { + // Make sure pipe exists in ops + if (!ops.hasOwnProperty("MapReduce") || + !ops['MapReduce'].hasOwnProperty(this.pipeName) || + typeof ops['MapReduce'][this.pipeName] !== 'object') + throw `MapReduce Error: Options object doesn't have MapReduce configurations / configurations are invalid.`; + + const pipe = ops['MapReduce'][this.pipeName]; + + // Make sure pipe has right data + if(!pipe.hasOwnProperty("map") || typeof pipe["map"] !== 'function') + throw `MapReduce Error: pipe does not have "map" function / parameter is not a valid function`; + if(!pipe.hasOwnProperty("reduce") || typeof pipe["reduce"] !== 'function') + throw `MapReduce Error: pipe does not have "reduce" function / parameter is not a valid function`; + if(!pipe.hasOwnProperty("reduceInitial")) + throw `MapReduce Error: pipe does not have "reduceInitial" value`; + + let innerContext = Object.assign({}, context); + let innerNode = this.children[0]; + const childResult = await innerNode.eval(innerContext, ops); + + if (!Array.isArray(childResult)) + throw `MapReduce Error: internal results is not an array, and thus cannot be MapReduced. Type is: ${typeof childResult}.`; + + const result = mapReduce(childResult, pipe["map"], pipe["reduce"], pipe["reduceInitial"]); + this.children = []; // Removing children so it doesn't recurse down the tree again. + return result; + } +} + +module.exports = MapReduceNode; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MapReduce/mapReduce.js b/src/Nodes/FunctionNodes/MapReduce/mapReduce.js new file mode 100644 index 0000000..fa362b9 --- /dev/null +++ b/src/Nodes/FunctionNodes/MapReduce/mapReduce.js @@ -0,0 +1,87 @@ +/** + * Created by iddo on 8/24/17. + */ + +/** + * Maps the array using the provided map function + * @param arr the array of objects to be mapped + * @param map map function. Accepts a single array item, and can return either a single item, or an array of items. Each item should have the structure {key: "", value: ...}. + * @returns {*} + */ +function mapStep(arr, map) { + return arr.map(map).reduce((prev, current) => { + return prev.concat(current); + },[]); +} + +/** + * Scatter an array into an object. From [{key:"a", value: "1"}, {key:"a", value: "2"}, {key:"b", value: "1"}] to {a: ["1", "2"], b: ["1"]} + * @param mapped Array of form [{key:"a", value: "1"}, ...] + * @returns {*} object of form {a: ["1", "2"], b: ["1"]} + */ +function scatterStep(mapped) { + return mapped.reduce((prev, current) => { + if (prev.hasOwnProperty(current["key"])) { + prev[current["key"]].push(current["value"]); + return prev; + } else { + prev[current["key"]] = [current["value"]]; + return prev; + } + }, {}); +} + +/** + * Applies they user provided reduce function + * @param grouped object of form {a: ["1", "2"], b: ["1"]} + * @param reduce reduce function f(previous, current) + * @param reduceInitial initial value to be used as previous when applying reduce. + * @returns {*} object of form {a: ReducedValue, b: ReducedValue} + */ +function reduceStep(grouped, reduce, reduceInitial = null) { + return Object.keys(grouped).reduce((previous, current) => { + previous[current] = grouped[current].reduce(reduce, reduceInitial); + return previous; + }, {}); +} + +function mapReduce(arr, map, reduce, reduceInitial) { + // Input validation step + if (!Array.isArray(arr)) + throw "First argument must be an array"; + else if (typeof map !== "function") + throw "Second argument must be a function (map function)"; + else if (typeof reduce !== "function") + throw "Third argument must be a function (reduce function)"; + + // Map step + const mapped = mapStep(arr, map); + /* users's map returns an array of {key: "", value: ...} objects, the reduce-concat step flattens it */ + + // Scatter step + const grouped = scatterStep(mapped); + + // Reduce Step + // This is why I'm using reduce and not map: https://stackoverflow.com/questions/14810506/map-function-for-objects-instead-of-arrays + const reduced = reduceStep(grouped, reduce, reduceInitial); + + return reduced; +} + +module.exports.mapReduce = mapReduce; +module.exports.mapStep = mapStep; +module.exports.scatterStep = scatterStep; +module.exports.reduceStep = reduceStep; + +// TEST +/*let strings = ["I want to eat", "I am hungry", "I am sleepy"]; + +let results = mapReduce(strings, (str) => { + return str.split(" ").map((word) => { + return {key:word, value: 1} + }); +}, (prev, current) => { + return prev + current; +}, 0); + +console.log(results);*/ \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MapReduce/test/MapReduceNode.js b/src/Nodes/FunctionNodes/MapReduce/test/MapReduceNode.js new file mode 100644 index 0000000..e19e781 --- /dev/null +++ b/src/Nodes/FunctionNodes/MapReduce/test/MapReduceNode.js @@ -0,0 +1,85 @@ +/** + * Created by iddo on 9/1/17. + */ +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const { expect, assert } = chai; +const MapReduceNode = require('./../MapReduceNode'); +const LeafNode = require('./../../../LeafNode'), + ArrayNode = require('./../../../ArrayNode'); + +module.exports = () => { + describe("MapReduceNode", () => { + describe("initial validation", () => { + it("should throw error when initialized with 0 children", () => { + assert.throws(() => { + new MapReduceNode("sdf", [], {}) + }, `MapReduce Error: node needs 1 child. 0 found.`); + }); + + it("should throw error when initialized without pipe name", () => { + assert.throws(() => { + new MapReduceNode("pipe", [new LeafNode("a")], {}) + }, `MapReduce Error: No pipe name. Should be MapReduce.pipeName(){}`); + }); + }); + + describe("runtime validations", () => { + const invalidNode = new MapReduceNode('MapReduce.pipe', [new LeafNode("a")], {}); + const invalidContext = {a:1}; + + it("should throw error if there are no MapReduce configurations", () => { + return expect(invalidNode.eval(invalidContext, {})).to.be.rejectedWith(`MapReduce Error: Options object doesn't have MapReduce configurations / configurations are invalid.`); + }); + + it("should throw error if MapReduce configurations doesn't have specific pipe", () => { + return expect(invalidNode.eval(invalidContext, {MapReduce: {}})).to.be.rejectedWith(`MapReduce Error: Options object doesn't have MapReduce configurations / configurations are invalid.`); + }); + + it("should throw error if MapReduce configurations doesn't have map function", () => { + return expect(invalidNode.eval(invalidContext, {MapReduce: {pipe: { + reduce: () => {}, + reduceInitial: 0 + }}})).to.be.rejectedWith(`MapReduce Error: pipe does not have "map" function / parameter is not a valid function`); + }); + + it("should throw error if MapReduce configuration's map isn't a function", () => { + return expect(invalidNode.eval(invalidContext, {MapReduce: {pipe: { + map: 1, + reduce: () => {}, + reduceInitial: 0 + }}})).to.be.rejectedWith(`MapReduce Error: pipe does not have "map" function / parameter is not a valid function`); + }); + + it("should throw error if MapReduce configurations doesn't have reduce function", () => { + return expect(invalidNode.eval(invalidContext, {MapReduce: {pipe: { + map: () => {}, + reduceInitial: 0 + }}})).to.be.rejectedWith(`MapReduce Error: pipe does not have "reduce" function / parameter is not a valid function`); + }); + + it("should throw error if MapReduce configuration's reduce isn't a function", () => { + return expect(invalidNode.eval(invalidContext, {MapReduce: {pipe: { + map: () => {}, + reduce: 1, + reduceInitial: 0 + }}})).to.be.rejectedWith(`MapReduce Error: pipe does not have "reduce" function / parameter is not a valid function`); + }); + + it("should throw error if MapReduce configurations doesn't have reduceInitial", () => { + return expect(invalidNode.eval(invalidContext, {MapReduce: {pipe: { + map: () => {}, + reduce: () => {} + }}})).to.be.rejectedWith(`MapReduce Error: pipe does not have "reduceInitial" value`); + }); + + it("should throw an error if MapReduce internal result is not an array", () => { + return expect(invalidNode.eval(invalidContext, {MapReduce: {pipe: { + map: () => {}, + reduce: () => {}, + reduceInitial: 0 + }}})).to.be.rejectedWith(`MapReduce Error: internal results is not an array, and thus cannot be MapReduced. Type is: ${typeof 1}.`); + }); + }); + }); +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MapReduce/test/mapReduce.js b/src/Nodes/FunctionNodes/MapReduce/test/mapReduce.js new file mode 100644 index 0000000..ab36cb0 --- /dev/null +++ b/src/Nodes/FunctionNodes/MapReduce/test/mapReduce.js @@ -0,0 +1,171 @@ +/** + * Created by iddo on 8/31/17. + */ +const { expect, assert } = require('chai'); +const {mapReduce, scatterStep, reduceStep, mapStep} = require('./../mapReduce'); +const identityFunction = a => a; + +module.exports = () => { + describe('Map Reduce Logic', () => { + describe('Map Step', () => { + it ('Should support empty arrays', () => { + assert.deepEqual([], mapStep([], identityFunction)); + }); + + it('shouldnt change array by mapping to identity function', () => { + let arr = ['avc', {a:1}, 13, null, undefined, 0]; + assert.deepEqual(arr, mapStep(arr, identityFunction)); + }); + + /** + * Checks that it actually performs function enough times + */ + it('should perform map function for each item', () => { + let count = 0; + function inc (a) { + count += a; + } + let arr = [1,2,3]; + + mapStep(arr, inc); + + assert.deepEqual(6, count); + + }); + + it('should return mapped array', () => { + let arr = ["a", "b", "c"]; + function map(a) { + return a+a; + } + assert.deepEqual(mapStep(arr, map), ["aa", "bb", "cc"]); + }); + + it('should flatten arrays', () => { + let arr = ["a", "b", "c"]; + function map(a) { + return [a+a, a+a]; + } + assert.deepEqual(mapStep(arr, map), ["aa", "aa", "bb", "bb", "cc", "cc"]); + }); + }); + + describe('Scatter Step', () => { + it('should scatter array', () => { + let arr = [ + {key: "a", value: 1}, + {key: "a", value: 2}, + {key: "a", value: "aa"}, + {key: "b", value: 10}, + {key: "b", value: null}, + {key: "c", value: {}} + ]; + + let scattered = { + a: [1,2,"aa"], + b: [10, null], + c: [{}] + }; + + assert.deepEqual(scatterStep(arr), scattered); + }); + }); + + describe('Reduce Step', () => { + it ('should reduce array', () => { + let src = { + a: [1,2,3], + b: [4,5,6], + c: [7] + }; + + let reduced = { + a: 6, + b: 15, + c: 7 + }; + + function reduce(prev, current) { + return prev + current; + } + + assert.deepEqual(reduceStep(src, reduce, 0), reduced); + + }); + }); + + describe ('Map Reduce', () => { + + it('Should throw error if not array (int)', (done) => { + try { + mapReduce(1); + done('Did not throw an error'); + } catch (e) { + assert.equal(e, "First argument must be an array"); + done(); + } + }); + + it('Should throw error if not array (object)', (done) => { + try { + mapReduce({}); + done('Did not throw an error'); + } catch (e) { + assert.equal(e, "First argument must be an array"); + done(); + } + }); + + it('Should throw error if not array (null)', (done) => { + try { + mapReduce(null); + done('Did not throw an error'); + } catch (e) { + assert.equal(e, "First argument must be an array"); + done(); + } + }); + + it ('should throw an error if map is not a function', (done) => { + try { + mapReduce([], []); + done('Did not throw an error'); + } catch (e) { + assert.equal(e, "Second argument must be a function (map function)"); + done(); + } + }); + + it ('should throw an error if reduce is not a function', (done) => { + try { + mapReduce([], identityFunction, 1); + done('Did not throw an error'); + } catch (e) { + assert.equal(e, "Third argument must be a function (reduce function)"); + done(); + } + }); + + it('Should count words in array of strings', () => { + let strings = ["I want to eat", "I am hungry", "I am sleepy"]; + let results = mapReduce(strings, (str) => { + return str.split(" ").map((word) => { + return {key:word, value: 1} + }); + }, (prev, current) => { + return prev + current; + }, 0); + let expectedResults = { + "I": 3, + "want": 1, + "to": 1, + "eat": 1, + "am": 2, + "hungry": 1, + "sleepy": 1 + }; + assert.deepEqual(results, expectedResults); + }); + }); + }); +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MongoDBDriver/MongoDBNode.js b/src/Nodes/FunctionNodes/MongoDBDriver/MongoDBNode.js new file mode 100644 index 0000000..eaaea2a --- /dev/null +++ b/src/Nodes/FunctionNodes/MongoDBDriver/MongoDBNode.js @@ -0,0 +1,75 @@ +/** + * Created by iddo on 7/19/17. + */ +const SEP_CHAR = '.'; + +const MongoClient = require('mongodb').MongoClient; + +const functions = { + find: require('./functions/find'), + sum: require('./functions/aggregate')('sum'), + avg: require('./functions/aggregate')('avg'), + min: require('./functions/aggregate')('min'), + max: require('./functions/aggregate')('max'), + insert: require('./functions/insert') +}; + +global._mongodb_clients = {}; +function getClient(DBName, DBConfigs) { + return new Promise((resolve, reject) => { + if (global._mongodb_clients.hasOwnProperty(DBName)) + resolve(global._mongodb_clients[DBName]); + else + MongoClient.connect(DBConfigs, (err, db) => { + if (err) + reject(`MongoDB: Connection error: ${err}`); + else { + global._mongodb_clients[DBName] = db; + resolve(global._mongodb_clients[DBName]); + } + }); + }); +} + +class MongoDBNode { + constructor(name, children, args) { + this.name = name; + this.args = args; + this.children = children; + } + + getName() { + return this.name; + } + + //noinspection JSAnnotator + async eval(context, ops) { + const self = this; + + const tokenizedName = this.name.split(SEP_CHAR); + // MongoDB.local.users.find() + const DBName = tokenizedName[1], + DBTable = tokenizedName[2], + operation = tokenizedName[3]; + + if(!functions.hasOwnProperty(operation)) + throw `Operation Error: operation ${operation} does not exist / is not supported`; + + //Create DB connection + //Check configs exist + if (!ops.hasOwnProperty('MongoDB')) { + throw `Missing configs: MongoDB settings are missing`; + } else if (!ops['MongoDB'].hasOwnProperty(DBName)) { + throw `Missing configs: MongoDB settings for DB ${DBName} are missing`; + } else { + const DBConfigs = ops['MongoDB'][DBName]; + + const db = await getClient(DBName, DBConfigs); + + //Route different functions + return await functions[operation](DBTable, db, self.args); + } + } +} + +module.exports = MongoDBNode; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MongoDBDriver/functions/aggregate.js b/src/Nodes/FunctionNodes/MongoDBDriver/functions/aggregate.js new file mode 100644 index 0000000..27cc2f6 --- /dev/null +++ b/src/Nodes/FunctionNodes/MongoDBDriver/functions/aggregate.js @@ -0,0 +1,34 @@ +/** + * Created by iddo on 7/21/17. + */ +const flattenObject = require('./../utils').flattenObject; +const convertObjectIds = require('./../utils').convertObjectIds; +const unconvertObjectIds = require('./../utils').unconvertObjectIds; +const removeSpecialArgs = require('./../utils').removeSpecialArgs; + + +const aggregate = (DBTable, db, args, aggregateFunction) => { + return new Promise((resolve, reject) => { + const field = args['FIELD']; + let query = flattenObject(convertObjectIds(removeSpecialArgs(args))); + db.collection(DBTable).aggregate([ + {$match:query}, + {$group: { + "_id": null, + [field]: {[aggregateFunction]: `$${field}`} + }} + ], (err, result) => { + if (err) { + reject(`MongoDB error performing aggregation: ${err}`); + } else { + resolve(result[0]); + } + }); + }); +}; + +module.exports = (aggregateFunction) => { + return function (DBTable, db, args) { + return aggregate(DBTable, db, args, `$${aggregateFunction}`); + } +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MongoDBDriver/functions/find.js b/src/Nodes/FunctionNodes/MongoDBDriver/functions/find.js new file mode 100644 index 0000000..f45bfdf --- /dev/null +++ b/src/Nodes/FunctionNodes/MongoDBDriver/functions/find.js @@ -0,0 +1,21 @@ +/** + * Created by iddo on 7/19/17. + */ + +const flattenObject = require('./../utils').flattenObject; +const convertObjectIds = require('./../utils').convertObjectIds; +const unconvertObjectIds = require('./../utils').unconvertObjectIds; + +module.exports = (DBTable, db, args) => { + return new Promise((resolve, reject) => { + let query = flattenObject(convertObjectIds(args)); + db.collection(DBTable).find(query).toArray((err, doc) => { + if (err) + reject(`MongoDB error performing find: ${err}`); + else { + let converted = unconvertObjectIds(doc); + resolve(converted); + } + }); + }); +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MongoDBDriver/functions/insert.js b/src/Nodes/FunctionNodes/MongoDBDriver/functions/insert.js new file mode 100644 index 0000000..c92a0c6 --- /dev/null +++ b/src/Nodes/FunctionNodes/MongoDBDriver/functions/insert.js @@ -0,0 +1,19 @@ +/** + * Created by iddo on 7/28/17. + */ +const flattenObject = require('./../utils').flattenObject; +const convertObjectIds = require('./../utils').convertObjectIds; +const unconvertObjectIds = require('./../utils').unconvertObjectIds; + +module.exports = (DBTable, db, args) => { + return new Promise((resolve, reject) => { + const newObj = args; + db.collection(DBTable).insertOne(newObj, (err, doc) => { + if (err) + reject(`MongoDB error performing insert: ${err}`); + else { + resolve(doc); + } + }); + }); +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MongoDBDriver/test/utils.js b/src/Nodes/FunctionNodes/MongoDBDriver/test/utils.js new file mode 100644 index 0000000..c1eeb82 --- /dev/null +++ b/src/Nodes/FunctionNodes/MongoDBDriver/test/utils.js @@ -0,0 +1,23 @@ +/** + * Created by iddo on 7/20/17. + */ +const assert = require('assert'), + flattenObject = require('./../utils').flattenObject, + reducers = require('./../utils').reducers; + + +module.exports = () => { + describe('flattenObject', () => { + it('should not change one level object', () => { + assert.deepEqual(flattenObject({a: 1, b: true, c: "asd"}), {a: 1, b: true, c: "asd"}); + }); + + it('should flatten two level object', () => { + assert.deepEqual(flattenObject({a: 1, b: true, c: "asd", o:{b:'c'}}), {a: 1, b: true, c: "asd", "o.b":'c'}); + }); + + it('should flatten n level object', () => { + assert.deepEqual(flattenObject({a: 1, b: true, c: "asd", o:{b:'c', d:{"c":"d"}}}), {a: 1, b: true, c: "asd", "o.b":'c', "o.d.c":"d" }); + }); + }); +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MongoDBDriver/utils.js b/src/Nodes/FunctionNodes/MongoDBDriver/utils.js new file mode 100644 index 0000000..f19bc5e --- /dev/null +++ b/src/Nodes/FunctionNodes/MongoDBDriver/utils.js @@ -0,0 +1,80 @@ +/** + * Created by iddo on 7/20/17. + */ +const flattenObject = (obj) => { + let toReturn = {}; + + for (let key in obj) { + if(obj.hasOwnProperty(key)) { + if(typeof obj[key] !== 'object') { + toReturn[key] = obj[key]; + } else if (obj[key]._bsontype !== 'ObjectID') { + let internalObj = flattenObject(obj[key]); + + for (let internalKey in internalObj) { + if (internalObj.hasOwnProperty(internalKey)) { + toReturn[`${key}.${internalKey}`] = internalObj[internalKey]; + } + } + } else { + toReturn[key] = obj[key]; + } + } + } + + return toReturn; +}; +module.exports.flattenObject = flattenObject; + + +const ObjectId = require('mongodb').ObjectId; +module.exports.ObjectId = ObjectId; + +/** + * This function takes a MongoDB query and converts {"$oid":"5dfg5k6jh4k645l6h4"} to ObjectIds. + * @param obj + * @returns {*} + */ +function convertObjectIds(obj) { + for (let key in obj) { + if (typeof obj[key] === 'object') + obj[key] = convertObjectIds(obj[key]); + else { + if (key === "$oid") + return ObjectId(obj[key]); + } + } + return obj; +} +module.exports.convertObjectIds = convertObjectIds; + + +/** + * This function takes a MongoDB document and converts ObjectIDs to Strings + * @param obj + * @returns {*} + */ +function unconvertObjectIds(obj) { + for (let key in obj) { + if(obj.hasOwnProperty(key) && obj[key] !== null && obj[key] !== undefined) + if (typeof obj[key] === 'object') { + if (obj[key]._bsontype == 'ObjectID') { + obj[key] = obj[key]+""; + } else { + obj[key] = unconvertObjectIds(obj[key]); + } + } + } + return obj; +} +module.exports.unconvertObjectIds = unconvertObjectIds; + +const specialKeys = ["FIELD"]; +function removeSpecialArgs(queryArgs) { + queryArgs = Object.assign({}, queryArgs); + specialKeys.forEach((key) => { + delete queryArgs[key]; + }); + return queryArgs; +} +module.exports.removeSpecialArgs = removeSpecialArgs; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/MySQLDriver/.DS_Store b/src/Nodes/FunctionNodes/MySQLDriver/.DS_Store deleted file mode 100644 index 5008ddf..0000000 Binary files a/src/Nodes/FunctionNodes/MySQLDriver/.DS_Store and /dev/null differ diff --git a/src/Nodes/FunctionNodes/MySQLDriver/MySQLNode.js b/src/Nodes/FunctionNodes/MySQLDriver/MySQLNode.js index 45737b0..60560ac 100644 --- a/src/Nodes/FunctionNodes/MySQLDriver/MySQLNode.js +++ b/src/Nodes/FunctionNodes/MySQLDriver/MySQLNode.js @@ -7,9 +7,22 @@ const mysql = require('mysql'); const functions = { find: require('./functions/find'), - count: require('./functions/count') + count: require('./functions/count'), + sum: require('./functions/sum'), + avg: require('./functions/avg') }; +global._mysql_clients = {}; +function getClient(DBName, DBConfigs) { + if (global._mysql_clients.hasOwnProperty(DBName)) { + return global._mysql_clients[DBName]; + } else { + const pool = mysql.createPool(DBConfigs); + global._mysql_clients[DBName] = pool; + return getClient(DBName, DBConfigs); + } +} + class MySQLNode { constructor(name, children, args) { this.name = name; @@ -44,8 +57,10 @@ class MySQLNode { return reject(`Missing configs: MySQL settings for DB ${DBName} are missing`); } else { const dbConfigs = ops.MySQL[DBName]; - const dbConnection = mysql.createConnection(dbConfigs); - dbConnection.connect((err) => { + + const pool = getClient(DBName, dbConfigs); + + pool.getConnection((err, dbConnection) => { if(err) { return reject(`DB Error: Error connecting to database ${DBName} -> ${err}`); } @@ -53,10 +68,28 @@ class MySQLNode { //Route different functions functions[operation](DBTable, dbConnection, self.args) .then((payload) => { + dbConnection.release(); resolve(payload); }) - .catch(reject); + .catch((err) => { + dbConnection.release(); + reject(err); + }); }); + + // const dbConnection = mysql.createConnection(dbConfigs); + // dbConnection.connect((err) => { + // if(err) { + // return reject(`DB Error: Error connecting to database ${DBName} -> ${err}`); + // } + // + // //Route different functions + // functions[operation](DBTable, dbConnection, self.args) + // .then((payload) => { + // resolve(payload); + // }) + // .catch(reject); + // }); } }); } diff --git a/src/Nodes/FunctionNodes/MySQLDriver/functions/avg.js b/src/Nodes/FunctionNodes/MySQLDriver/functions/avg.js new file mode 100644 index 0000000..3c4c4f2 --- /dev/null +++ b/src/Nodes/FunctionNodes/MySQLDriver/functions/avg.js @@ -0,0 +1,38 @@ +/** + * Created by iddo on 7/21/17. + */ +const whereGenerator = require('./../whereGenerator'); + +function avg(DBTable, client, args) { + //We'll build the SQL query with that string + let queryString = ""; + + //GROUP BY + //If we're grouping, we want to select the group name as well + let coloumns = ""; + if (typeof args['GROUPBY'] == 'string') { + coloumns += `, ${args['GROUPBY']}`; + } + + if (!args['FIELD']) + return new Promise((resolve, reject) => {reject(`MySQL: to use the .sum() function, must supply FIELD to sum`)}); + + //Base query + queryString += `SELECT AVG(${args['FIELD']}) as ${args['FIELD']} ${coloumns} FROM \`${DBTable}\``; + + //Add where conditions + queryString += whereGenerator.whereGenerator(args); + + return new Promise((resolve, reject) => { + client.query(queryString, (err, result) => { + if (err) { + reject(err); + } else { + resolve(result || []); + } + }); + }); + +} + +module.exports = avg; diff --git a/src/Nodes/FunctionNodes/MySQLDriver/functions/count.js b/src/Nodes/FunctionNodes/MySQLDriver/functions/count.js index dc4e579..3be3100 100644 --- a/src/Nodes/FunctionNodes/MySQLDriver/functions/count.js +++ b/src/Nodes/FunctionNodes/MySQLDriver/functions/count.js @@ -15,10 +15,10 @@ function count(DBTable, client, args) { } //Base query - queryString += `SELECT COUNT(*) ${coloumns} FROM ${DBTable}`; + queryString += `SELECT COUNT(*) ${coloumns} FROM \`${DBTable}\``; //Add where conditions - queryString += whereGenerator(args); + queryString += whereGenerator.whereGenerator(args); return new Promise((resolve, reject) => { client.query(queryString, (err, result) => { @@ -36,4 +36,4 @@ function count(DBTable, client, args) { } -module.exports = count; \ No newline at end of file +module.exports = count; diff --git a/src/Nodes/FunctionNodes/MySQLDriver/functions/find.js b/src/Nodes/FunctionNodes/MySQLDriver/functions/find.js index 763021b..de43185 100644 --- a/src/Nodes/FunctionNodes/MySQLDriver/functions/find.js +++ b/src/Nodes/FunctionNodes/MySQLDriver/functions/find.js @@ -9,10 +9,10 @@ function find(DBTable, client, args) { let queryString = ""; //Base query - queryString += `SELECT * FROM ${DBTable}`; + queryString += `SELECT * FROM \`${DBTable}\``; //Add where conditions - queryString += whereGenerator(args); + queryString += whereGenerator.whereGenerator(args); return new Promise((resolve, reject) => { client.query(queryString, (err, result) => { @@ -26,4 +26,4 @@ function find(DBTable, client, args) { } -module.exports = find; \ No newline at end of file +module.exports = find; diff --git a/src/Nodes/FunctionNodes/MySQLDriver/functions/sum.js b/src/Nodes/FunctionNodes/MySQLDriver/functions/sum.js new file mode 100644 index 0000000..1334618 --- /dev/null +++ b/src/Nodes/FunctionNodes/MySQLDriver/functions/sum.js @@ -0,0 +1,39 @@ +/** + * Created by iddo on 7/21/17. + */ + +const whereGenerator = require('./../whereGenerator'); + +function sum(DBTable, client, args) { + //We'll build the SQL query with that string + let queryString = ""; + + //GROUP BY + //If we're grouping, we want to select the group name as well + let coloumns = ""; + if (typeof args['GROUPBY'] == 'string') { + coloumns += `, ${args['GROUPBY']}`; + } + + if (!args['FIELD']) + return new Promise((resolve, reject) => {reject(`MySQL: to use the .sum() function, must supply FIELD to sum`)}); + + //Base query + queryString += `SELECT SUM(${args['FIELD']}) as ${args['FIELD']} ${coloumns} FROM \`${DBTable}\``; + + //Add where conditions + queryString += whereGenerator.whereGenerator(args); + + return new Promise((resolve, reject) => { + client.query(queryString, (err, result) => { + if (err) { + reject(err); + } else { + resolve(result || []); + } + }); + }); + +} + +module.exports = sum; diff --git a/src/Nodes/FunctionNodes/MySQLDriver/utils.js b/src/Nodes/FunctionNodes/MySQLDriver/utils.js new file mode 100644 index 0000000..1642709 --- /dev/null +++ b/src/Nodes/FunctionNodes/MySQLDriver/utils.js @@ -0,0 +1,54 @@ +/** + * Created by iddo on 7/21/17. + */ +/** + * Created by iddo on 6/28/17. + */ +/** + * Remove special selectors (LIMIT, ORDERBY etc...) from query arguments + * @param queryArgs + * @returns {*} + */ +function removeSpecialArgs(queryArgs, _specialKeys) { + queryArgs = Object.assign({}, queryArgs); + _specialKeys.forEach((key) => { + delete queryArgs[key]; + }); + return queryArgs; +} + +/** + * Returns true if val is a number / string with only a number + * @param val + * @returns {boolean} + */ +function isNumberParseable(val) { + return (!isNaN(parseFloat(val)) && `${parseFloat(val)}` === val) || (typeof val == 'number'); +} + +/** + * Return a string. If value is a string, it'll be quoted. If it's a number or can be parsed as one - it won't. + * @param value + * @returns {string} + */ +function quoteAsNeeded(value) { + return (typeof value == 'number') ? `${value}` : + (isNumberParseable(value)) ? `${parseFloat(value)}` : + (typeof value == 'string') ? `'${value.replace(/'/g, "''")}'` + : `'${value}'`; +} + +/** + * Return an objects values as an array (pollyfill for ES2017's Object.values) + * @param obj + * @returns {Array} + */ +function objValues(obj) { + return Object.keys(obj).map(key => obj[key]); + +} + +module.exports.removeSpecialArgs = removeSpecialArgs; +module.exports.isNumberParseable = isNumberParseable; +module.exports.quoteAsNeeded = quoteAsNeeded; +module.exports.objValues = objValues; diff --git a/src/Nodes/FunctionNodes/MySQLDriver/whereGenerator.js b/src/Nodes/FunctionNodes/MySQLDriver/whereGenerator.js index cc7268a..6624910 100644 --- a/src/Nodes/FunctionNodes/MySQLDriver/whereGenerator.js +++ b/src/Nodes/FunctionNodes/MySQLDriver/whereGenerator.js @@ -1,25 +1,17 @@ -const specialKeys = ['WHERE', 'LIMIT', 'ORDERBY', 'SKIP', "GROUPBY"]; +const specialKeys = ['WHERE', 'LIMIT', 'ORDERBY', 'SKIP', "GROUPBY", "SET", "FIELD"]; + +//Import utility functions +const removeSpecialArgs = require('./utils').removeSpecialArgs; +const quoteAsNeeded = require('./utils').quoteAsNeeded; + /** - * This function takes query arguments and turns them into a PostgreSQL WHERE clause + * Generates the "WHERE x=y..." part of the where query * @param queryArgs + * @returns {string} */ -function whereGenerator(args) { +function whereClauseGenerator(queryArgs) { let queryString = ""; - - //Check if full query / shorthand - let queryArgs ; - if (args.hasOwnProperty('WHERE') && typeof args['WHERE'] == 'object') - queryArgs = Object.assign({},args['WHERE']); - else - queryArgs = Object.assign({},args); - - //Remove special instructions (LIMIT, SKIP, ETC...) - specialKeys.forEach((key) => { - delete queryArgs[key]; - }); - - //WHERE clause if (Object.keys(queryArgs).length > 0) { queryString += ` WHERE`; //Has one is set to true after first condition is added, to append the AND keyword @@ -31,20 +23,44 @@ function whereGenerator(args) { //Simple equality operator if (typeof queryArgs[field] == 'string' || typeof queryArgs[field] == 'number') { - queryString += ` ${field} = '${queryArgs[field]}'` + queryString += ` ${field} = ${quoteAsNeeded(queryArgs[field])}`; //Complex inequalities } else if (typeof queryArgs[field] == 'object') { //If comparators have quotes - remove const compOp = Object.keys(queryArgs[field])[0]; const compVal = queryArgs[field][Object.keys(queryArgs[field])[0]]; - queryString += ` ${field} ${compOp} ${compVal}`; + queryString += ` ${field} ${compOp} ${quoteAsNeeded(compVal)}`; } hasOne = true; } } } + return queryString; +} + + +/** + * This function takes query arguments and turns them into a PostgreSQL WHERE clause + * @param args the input arguments + */ +function whereGenerator(args) { + let queryString = ""; + + //Check if full query / shorthand + let queryArgs ; + if (args.hasOwnProperty('WHERE') && typeof args['WHERE'] == 'object') + queryArgs = Object.assign({},args['WHERE']); + else + queryArgs = Object.assign({},args); + + //Remove special instructions (LIMIT, SKIP, ETC...) + queryArgs = removeSpecialArgs(queryArgs, specialKeys); + + //WHERE clause + queryString += whereClauseGenerator(queryArgs); + //GROUP BY clause if (typeof args['GROUPBY'] == 'string') { //Shorthand queryString += ' GROUP BY ' + args['GROUPBY']; @@ -83,4 +99,5 @@ function whereGenerator(args) { return queryString; } -module.exports = whereGenerator; \ No newline at end of file +module.exports.whereGenerator = whereGenerator; +module.exports.whereClauseGenerator = whereClauseGenerator; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/PostgreSQLDriver/PostgreSQLNode.js b/src/Nodes/FunctionNodes/PostgreSQLDriver/PostgreSQLNode.js index 0cc087f..2e9cc9c 100644 --- a/src/Nodes/FunctionNodes/PostgreSQLDriver/PostgreSQLNode.js +++ b/src/Nodes/FunctionNodes/PostgreSQLDriver/PostgreSQLNode.js @@ -13,18 +13,22 @@ const functions = { count: require('./functions/count'), insert: require('./functions/insert'), softInsert: require('./functions/softInsert'), - update: require('./functions/update') + update: require('./functions/update'), + avg: require('./functions/aggregate')('avg'), + sum: require('./functions/aggregate')('sum'), + max: require('./functions/aggregate')('max'), + min: require('./functions/aggregate')('min') }; -global.clients = {}; +global._postgresql_clients = {}; function getClient(DBName, DBConfigs) { // This is basically a singleton. If we already have a client, we should use it. // Otherwise, we create a new one. - if (clients.hasOwnProperty(DBName)) { - return global.clients[DBName]; + if (global._postgresql_clients.hasOwnProperty(DBName)) { + return global._postgresql_clients[DBName]; } else { const pool = new pg.Pool(DBConfigs); - global.clients[DBName] = pool; + global._postgresql_clients[DBName] = pool; return getClient(DBName, DBConfigs); } } diff --git a/src/Nodes/FunctionNodes/PostgreSQLDriver/functions/aggregate.js b/src/Nodes/FunctionNodes/PostgreSQLDriver/functions/aggregate.js new file mode 100644 index 0000000..f63019b --- /dev/null +++ b/src/Nodes/FunctionNodes/PostgreSQLDriver/functions/aggregate.js @@ -0,0 +1,43 @@ +/** + * Created by iddo on 7/21/17. + */ + +const whereGenerator = require('./../whereGenerator').whereGenerator; + +function aggregate(DBSchema, DBTable, client, args, aggregateFunction) { + //We'll build the SQL query with that string + let queryString = ""; + + //GROUP BY + //If we're grouping, we want to select the group name as well + let coloumns = ""; + if (typeof args['GROUPBY'] == 'string') { + coloumns += `, ${args['GROUPBY']}`; + } + + if (!args['FIELD']) + return new Promise((resolve, reject) => {reject(`PostgreSQL: to use the ${aggregateFunction}() aggregate function, must supply FIELD to aggregate`)}); + + //Base query + queryString += `SELECT ${aggregateFunction.toUpperCase()}(${args['FIELD']}) as ${args['FIELD']} ${coloumns} FROM ${DBSchema}.${DBTable}`; + + //Add where conditions + queryString += whereGenerator(args); + + return new Promise((resolve, reject) => { + client.query(queryString, (err, result) => { + if (err) { + reject(err); + } else { + resolve(result.rows || []); + } + }); + }); + +} + +module.exports = (aggregateFunction) => { + return function (DBSchema, DBTable, client, args) { + return aggregate(DBSchema, DBTable, client, args, aggregateFunction); + } +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/PostgreSQLDriver/whereGenerator.js b/src/Nodes/FunctionNodes/PostgreSQLDriver/whereGenerator.js index 6fa5e16..6624910 100644 --- a/src/Nodes/FunctionNodes/PostgreSQLDriver/whereGenerator.js +++ b/src/Nodes/FunctionNodes/PostgreSQLDriver/whereGenerator.js @@ -1,4 +1,4 @@ -const specialKeys = ['WHERE', 'LIMIT', 'ORDERBY', 'SKIP', "GROUPBY", "SET"]; +const specialKeys = ['WHERE', 'LIMIT', 'ORDERBY', 'SKIP', "GROUPBY", "SET", "FIELD"]; //Import utility functions const removeSpecialArgs = require('./utils').removeSpecialArgs; diff --git a/src/Nodes/FunctionNodes/RapidAPIDriver/RapidAPINode.js b/src/Nodes/FunctionNodes/RapidAPIDriver/RapidAPINode.js index 7f7b790..82be01e 100644 --- a/src/Nodes/FunctionNodes/RapidAPIDriver/RapidAPINode.js +++ b/src/Nodes/FunctionNodes/RapidAPIDriver/RapidAPINode.js @@ -55,7 +55,7 @@ class RapidAPINode { resolve(payload); }) .on('error', (err) => { - reject(`APIError: got error from ${this.getName()} API: ${err}`); + reject(`APIError: got error from ${this.getName()} API: ${typeof err === 'object' ? JSON.stringify(err) : err}`); }); }); } diff --git a/src/Nodes/FunctionNodes/RedisDriver/RedisNode.js b/src/Nodes/FunctionNodes/RedisDriver/RedisNode.js new file mode 100644 index 0000000..5bc9e2d --- /dev/null +++ b/src/Nodes/FunctionNodes/RedisDriver/RedisNode.js @@ -0,0 +1,75 @@ +/** + * Created by iddo on 7/19/17. + */ +const SEP_CHAR = '.'; + +const redis = require("redis"); + +global._redis_clients = {}; +function getClient(DBName, DBConfig) { + if (global._redis_clients.hasOwnProperty(DBName)) + return global._redis_clients[DBName]; + else { + global._redis_clients[DBName] = redis.createClient(DBConfig); + return getClient(DBName, DBConfig); + } +} + + +// Types: +const string = require('./types/string'); +const hash = require('./types/hash'); +const generic = require('./types/generic'); +const functions = { + get : string.get, + set : string.set, + hmset : hash.hmset, + hgetall : hash.hgetall, + type : generic.type, + find : generic.find +}; + + + +class RedisNode { + constructor(name, children, args) { + this.name = name; + this.args = args; + this.children = children; + } + + getName() { + return this.name; + } + + async eval(context, ops) { + const self = this; + + const tokenizedName = this.name.split(SEP_CHAR); + const DBName = tokenizedName[1], + operation = tokenizedName[2]; + + //Check operation exists + if (!functions.hasOwnProperty(operation)) + throw `Operation Error: operation ${operation} does not exist / is not supported`; + + + //Create DB connection + //Check configs exist + if (!ops.hasOwnProperty('Redis')) { + throw `Missing configs: Redis settings are missing`; + } else if (!ops['Redis'].hasOwnProperty(DBName)) { + throw `Missing configs: Redis settings for DB ${DBName} are missing`; + } else { + const DBConfigs = ops['Redis'][DBName]; + + const client = getClient(DBName, DBConfigs); + + //Route different functions + const res = await functions[operation](client, self.args); + return res; + } + } +} + +module.exports = RedisNode; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/RedisDriver/types/generic.js b/src/Nodes/FunctionNodes/RedisDriver/types/generic.js new file mode 100644 index 0000000..6583cb2 --- /dev/null +++ b/src/Nodes/FunctionNodes/RedisDriver/types/generic.js @@ -0,0 +1,33 @@ +/** + * Created by iddo on 7/19/17. + */ + + +const string = require('./string'), + hash = require('./hash'); + +function getType(client, key) { + return new Promise((resolve, reject) => { + client.type(key, (err, data) => { + if (err) + reject(`Redis error getting type for key ${key}: ${err}`); + else + resolve(data); + }); + }); +} +module.exports.type = (client, args) => { + return getType(client, args['key']); +}; + +module.exports.find = async (client, args) => { + // Preflight type check + const type = await getType(client, args['key']); + + if (type === 'string') + return await string.get(client, args); + else if (type === 'hash') + return await hash.hgetall(client, args); + else + throw `Redis operation find not supported on type ${type}`; +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/RedisDriver/types/hash.js b/src/Nodes/FunctionNodes/RedisDriver/types/hash.js new file mode 100644 index 0000000..2cc892a --- /dev/null +++ b/src/Nodes/FunctionNodes/RedisDriver/types/hash.js @@ -0,0 +1,36 @@ +/** + * Created by iddo on 7/19/17. + */ + +const getArg = require('./../utils').getArg; +const flattenObject = require('./../utils').flattenObject; + +function hgetall (client, key) { + return new Promise((resolve, reject) => { + client.hgetall(key, (err, data) => { + if (err) + reject(`Redis error getting key ${args[key]}: ${err}`); + else + resolve(data); + }); + }); +} + +module.exports.hgetall = async (client, args) => { + return await hgetall(client, await getArg(args, 'key')); +}; + +function hmset (client, key, value) { + return new Promise((resolve, reject) => { + client.hmset(key, ...flattenObject(value), (err, data) => { + if (err) + reject(`Redis error setting key ${key}: ${err}`); + else + resolve(data); + }); + }); +} + +module.exports.hmset = async (client, args) => { + return await hmset(client, await getArg(args, 'key'), await getArg(args, 'value')); +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/RedisDriver/types/string.js b/src/Nodes/FunctionNodes/RedisDriver/types/string.js new file mode 100644 index 0000000..08b23cd --- /dev/null +++ b/src/Nodes/FunctionNodes/RedisDriver/types/string.js @@ -0,0 +1,33 @@ +/** + * Created by iddo on 7/19/17. + */ + +const getArg = require('./../utils').getArg; + +function get(client, key) { + return new Promise((resolve, reject) => { + client.get(key, (err, data) => { + if (err) + reject(`Redis error getting key ${key}: ${err}`); + else + resolve({value:data}); + }); + }); +} +module.exports.get = async (client, args) => { + return await get(client, await getArg(args, 'key')); +}; + +function set(client, key, value) { + return new Promise((resolve, reject) => { + client.set(key, value, (err, data) => { + if (err) + reject(`Redis error setting key ${key} with value ${value}: ${err}`); + else + resolve(data); + }); + }); +} +module.exports.set = async (client, args) => { + return await set(client, await getArg(args, 'key'), await getArg(args, 'value')); +}; \ No newline at end of file diff --git a/src/Nodes/FunctionNodes/RedisDriver/utils.js b/src/Nodes/FunctionNodes/RedisDriver/utils.js new file mode 100644 index 0000000..513c0a0 --- /dev/null +++ b/src/Nodes/FunctionNodes/RedisDriver/utils.js @@ -0,0 +1,11 @@ +/** + * Created by iddo on 7/19/17. + */ +module.exports.getArg = async (obj, key) => { + if (!obj['key']) + throw `Redis error - "${key}" is required`; + else + return obj[key]; +}; + +module.exports.flattenObject = obj => Object.keys(obj).reduce((r, k) => {return r.concat(k, obj[k]);}, []); \ No newline at end of file diff --git a/src/Nodes/LeafNode.js b/src/Nodes/LeafNode.js index b45e022..db0b479 100644 --- a/src/Nodes/LeafNode.js +++ b/src/Nodes/LeafNode.js @@ -2,6 +2,7 @@ * Created by Iddo on 12/17/2016. */ "use strict"; +const utils = require('./utils'); class LeafNode { constructor(name) { @@ -14,10 +15,7 @@ class LeafNode { //noinspection JSAnnotator async eval(context) { - if (context.hasOwnProperty(this.getName())) - return context[this.getName()]; - else - throw `Name ${this.getName()} does not exist in context ${context}`; + return utils.resolve(this.getName(), context); } } diff --git a/src/Nodes/LogicNode.js b/src/Nodes/LogicNode.js new file mode 100644 index 0000000..d54ff71 --- /dev/null +++ b/src/Nodes/LogicNode.js @@ -0,0 +1,148 @@ +/** + * Created by iddo on 9/22/18. + */ +const SEP_CHAR = '.'; + +const FunctionNode = require('./FunctionNode'); // Uses recursive replace from there +const ObjectNode = require('./ObjectNode'); +const { createMixedContext } = require('./utils'); + +function __if(args, _) { + const val1 = args['val1']; + const val2 = args['val2']; + const comparison = args['comparison'] || "=="; + + switch (comparison) { + case "==": + return val1 == val2; + break; + case "!=": + return val1 != val2; + break; + case ">": + return val1 > val2; + break; + case "<": + return val1 < val2; + break; + case ">=": + return val1 >= val2; + break; + case "<=": + return val1 <= val2; + break; + case "set": + return !!val1; + break; + case "prefixes": //val1 prefixes val2 + if (`${val1}`.length > `${val2}`.length) return false; + return `${val1}` === `${val2}`.substr(0, val1.length); + break; + case "notSet": + return !val1; + break; + case "in": + if (!Array.isArray(val2)) + throw `ComparisonError: for 'in' comparison, val1 should be an array. Got ${typeof val2} (value: ${val2}).`; + return val2.indexOf(val1) >= 0; + break; + case "notIn": + if (!Array.isArray(val2)) + throw `ComparisonError: for 'in' comparison, val1 should be an array. Got ${typeof val2} (value: ${val2}).`; + return val2.indexOf(val1) < 0; + break; + case "prefixIn": + if (!Array.isArray(val2)) + throw `ComparisonError: for 'in' comparison, val1 should be an array. Got ${typeof val2} (value: ${val2}).`; + return val2.map(v2 => `${v2}` === `${val1}`.substr(0, v2.length)).filter(v2 => !!v2).length > 0; + break; + case "prefixNotIn": + if (!Array.isArray(val2)) + throw `ComparisonError: for 'in' comparison, val1 should be an array. Got ${typeof val2} (value: ${val2}).`; + return val2.map(v2 => `${v2}` === `${val1}`.substr(0, v2.length)).filter(v2 => !!v2).length === 0; + break; + } +} + +function __prefix(args) { + return __if({ + val1 : args['prefix'], + val2 : args['string'], + comparison : 'prefixes' + }) +} + +function __equal(args) { + return __if({ + val1 : args['val1'], + val2 : args['val2'], + comparison : '==' + }) +} + +const __ops = { + "if" : __if, + "prefix" : __prefix, + "equal" : __equal, + "else" : () => true, + "elseif" : __if +}; + +class LogicNode { + constructor(name, children, args, followOnNode) { + this.name = name; + this.args = args; + this.children = children; + + if (followOnNode && !(followOnNode.getName() === "@elseif" || followOnNode.getName() === "@else")) + throw `Logic Node following an @if node must be either @elseif or @else, got: ${followOnNode.getName()}`; + + this.followOnNode = followOnNode; //Follow on node will be an @elseif or @else node + } + + getName() { + return `@${this.name.slice(0,-1)}`; //Get rid of preceding dot + } + + /** + * Process arguments and replace variables based on context + * @param context + * @returns {{}} + */ + getProcessedArgs(context) { + //Process args in context + //If they have " " -> string literal (remove quotes) + //If they don't -> fetch from context + return require('./FunctionNode').recursiveReplace(this.args, context); //Weird issues with using the FunctionNode object. To be explored + } + + async eval(context, ops) { + + const operation = this.getName().slice(1); + + try { + + const result = __ops[operation](this.getProcessedArgs(context), null); + + if (!result) { + if (this.followOnNode) { + return await this.followOnNode.eval(context, ops); + } + return null; + } + + let innerContext = createMixedContext(context, { + [this.getName()] : {} + }); + let innerNode = new ObjectNode(this.getName(), this.children); + return await innerNode.eval(innerContext, ops); + + } catch (e) { + throw `Error in ${this.getName()}: ${e}`; + } + } +} + +LogicNode.logicFunctions = __ops; + +module.exports = LogicNode; \ No newline at end of file diff --git a/src/Nodes/ObjectNode.js b/src/Nodes/ObjectNode.js index 5555d18..8d85d52 100644 --- a/src/Nodes/ObjectNode.js +++ b/src/Nodes/ObjectNode.js @@ -3,6 +3,8 @@ */ "use strict"; +const { createMixedContext } = require('./utils'); + class ObjectNode { constructor(name, children) { this.name = name; @@ -13,15 +15,20 @@ class ObjectNode { return this.name; } + //noinspection JSAnnotator eval(context, ops) { - const ctx = Object.assign({}, context, context[this.getName()]); + const ctx = createMixedContext(context, context[this.getName()] || {}); let res = {}; let promises = []; this.children.forEach((child) => { promises.push(new Promise((resolve, reject) => { child.eval(ctx, ops) .then((val) => { - res[child.getName()] = val; + // Treatment for FlatObjectNodes - need to add multiple properties + if (child.constructor.name === 'FlatObjectNode' && typeof val === 'object') + res = Object.assign(res, val); + else + res[child.getName()] = val; resolve(); }) .catch(reject); diff --git a/src/Nodes/utils.js b/src/Nodes/utils.js new file mode 100644 index 0000000..9d98d2e --- /dev/null +++ b/src/Nodes/utils.js @@ -0,0 +1,66 @@ +/** + * Created by iddo on 8/31/17. + */ + +/** + * Find an object's property from a path + * @param path dot notation path (a.b.c) + * @param object object for property to be pulled from + * @returns {*} + */ +module.exports.resolve = (path, object) => { + if (object.hasOwnProperty(path)) + return object[path]; + return path.split('.').reduce(function (prev, curr) { + if (prev) + //if (prev.hasOwnProperty(curr)) + if (prev[curr] !== undefined) + return prev[curr]; + throw `Name ${path} does not exist in context ${JSON.stringify(object, null, 4)}`; + }, object); +}; + +/** + * Create an object that is a combination of an inner context and outer context. Will strive to return elements from inner context, defaulting to outer if not in inner + * @param outerContext + * @param innerContext + * @returns {Proxy} + */ +module.exports.createMixedContext = (outerContext, innerContext) => { + + if (typeof innerContext != 'object' || typeof outerContext != 'object') + throw `Contexts must be of object type`; + + const handler = { + get: (target, name) => { + // Lookup order: innerContext > outerContext > undefined + // JS is ugly. Using "in" instead of !== undefined would have been MUCH nicer, but in is not supported with Proxy... (WHY????) + return target.innerContext[name] !== undefined ? target.innerContext[name] + : target.outerContext[name] !== undefined ? target.outerContext[name] + : undefined; + }, + has: (target, name) => { + return name in target.innerContext || name in target.outerContext; + }, + getOwnPropertyDescriptor: (target, name) => { + if (name in target.innerContext || name in target.outerContext) { + return { + enumerable : true, + configurable : true, + writable : true + }; + } + return undefined; + } + }; + + // Add support for native functions + // Proxy.prototype.hasOwnProperty = function (name) { + // return this[name] !== undefined ? this[name] : false; + // }; + + return new Proxy({ + innerContext, + outerContext + }, handler); +}; \ No newline at end of file diff --git a/src/Parser/Parser.js b/src/Parser/Parser.js index 897734e..64c522c 100644 --- a/src/Parser/Parser.js +++ b/src/Parser/Parser.js @@ -6,17 +6,28 @@ const nearley = require("nearley"), grammer = require("./grammer"), peg = require("pegjs"); + const WHITE_SPACES = [ ' ', '\n', '\t' ]; +function removeComments(str) { + str = str.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '');; + return str; +} +function removeWhiteSpaces(str) { + str = str.replace("\n", "").replace("\t", ""); + return str.replace(/\s+(?=((\\[\\"]|[^\\"])*"(\\[\\"]|[^\\"])*")*(\\[\\"]|[^\\"])*$)/g, ''); +} +module.exports.removeWhiteSpaces = removeWhiteSpaces; +module.exports.removeComments = removeComments; module.exports.parse = (str) => { + //Find and replace comments + str = removeComments(str); //Find and replace spaces - WHITE_SPACES.forEach((whiteSpace) => { - str = str.replace(new RegExp(whiteSpace, "g"), ''); - }); + str = removeWhiteSpaces(str); return new Promise((resolve, reject) => { try { diff --git a/src/Parser/grammer-raw.pegjs b/src/Parser/grammer-raw.pegjs index e30572e..417e4c0 100644 --- a/src/Parser/grammer-raw.pegjs +++ b/src/Parser/grammer-raw.pegjs @@ -14,6 +14,8 @@ Complex = "{" firstNode:Node? nodes:("," Node)* "}" { Node = RenameNode / OptionalNode + / LogicNode + / CachedFunctionNode / FunctionNode / CompositeNode / CastedLeafNode @@ -37,6 +39,14 @@ CompositeNode = label:Word values:Complex { return {'label' : label, 'value': values}; } +LogicNode = "@" n:FunctionNode f:LogicNode?{ + return {'t':'logic', 'l':n.label, 'a':n.args,'f':f}; +} + +CachedFunctionNode = "*" n:FunctionNode { + return {type: 'cached', value:n} +} + FunctionNode = label:Word args:ArgSet values:Complex { return {'label': label, 'args': args, 'value': values}; } @@ -58,8 +68,19 @@ KVTuple = key:Word ":" value:KVValue { } //Added new intermidiate type to support (key:{subkey:value}) -KVValue = ValueWord / Word / KVCompValue +KVValue = Number / ValueWord / Word / KVCompValue / KVArray +//Support array parameters +KVArray = "[" el0:KVValue? els:("," value:KVValue)* "]" { + let res = []; + if (el0) { + res[0] = el0; + els.forEach(function(e) { + res.push(e[1]); + }); + } + return res; +} //This support having json types in args KVCompValue = "{}" {return {};} //empty @@ -72,10 +93,37 @@ KVCompValue = "{}" {return {};} //empty return rs; } -Word = chars:[-<*=>@_0-9"'a-zA-Z.]+ { +Word = chars:[-$!<=>@_0-9a-zA-Z.]+ { return chars.join(""); +} / str:StringLiteralValue { + return str.slice(1,-1); } -ValueWord = '"' chars:[-<*=>,{}@_0-9\?/:a-zA-Z.]+ '"' { - return '"' + chars.join("") + '"'; -} \ No newline at end of file +ValueWord = StringLiteralValue + +Number = sign:"-"? chars:[-.0-9]+ { + return parseFloat([sign].concat(chars).join("")); +} + +StringLiteralValue + = '"' chars:DoubleStringCharacter* '"' { return '"' +chars.join('') + '"'; } + / "'" chars:SingleStringCharacter* "'" { return '"' +chars.join('') + '"'; } + +DoubleStringCharacter + = !('"' / "\\") char:. { return char; } + / "\\" sequence:EscapeSequence { return sequence; } + +SingleStringCharacter + = !("'" / "\\") char:. { return char; } + / "\\" sequence:EscapeSequence { return sequence; } + +EscapeSequence + = "'" + / '"' + / "\\" + / "b" { return "\b"; } + / "f" { return "\f"; } + / "n" { return "\n"; } + / "r" { return "\r"; } + / "t" { return "\t"; } + / "v" { return "\x0B"; } diff --git a/src/Parser/grammer.js b/src/Parser/grammer.js index e319560..e946909 100644 --- a/src/Parser/grammer.js +++ b/src/Parser/grammer.js @@ -169,36 +169,56 @@ function peg$parse(input, options) { const OptionalNode = require('./../Nodes/OptionalNode') return new OptionalNode(innerNode); }, - peg$c13 = "(", - peg$c14 = peg$literalExpectation("(", false), - peg$c15 = ")", - peg$c16 = peg$literalExpectation(")", false), - peg$c17 = function(cast, innerNode) { + peg$c13 = "-", + peg$c14 = peg$literalExpectation("-", false), + peg$c15 = function(innerNode) { + const FlatObjectNode = require('./../Nodes/FlatObjectNode') + return new FlatObjectNode(innerNode); + }, + peg$c16 = "(", + peg$c17 = peg$literalExpectation("(", false), + peg$c18 = ")", + peg$c19 = peg$literalExpectation(")", false), + peg$c20 = function(cast, innerNode) { const CastedLeafNode = require('./../Nodes/CastedLeafNode'); return new CastedLeafNode(cast, innerNode); }, - peg$c18 = function(label) { + peg$c21 = function(label) { const LeafNode = require('./../Nodes/LeafNode'), CompositeNode = require('./../Nodes/CompositeNode'), FunctionNode = require('./../Nodes/FunctionNode'); return new LeafNode(label); //return label; }, - peg$c19 = function(label, values) { + peg$c22 = function(label, values) { const LeafNode = require('./../Nodes/LeafNode'), CompositeNode = require('./../Nodes/CompositeNode'), FunctionNode = require('./../Nodes/FunctionNode'); return new CompositeNode(label, values); //return {'label' : label, 'value': values}; }, - peg$c20 = function(label, args, values) { + peg$c23 = "@", + peg$c24 = peg$literalExpectation("@", false), + peg$c25 = function(n, f) { + const LogicNode = require('./../Nodes/LogicNode'); + return new LogicNode(n.getName(), n.children, n.args, f); + //return {'t':'logic', 'l':n.label, 'a':n.args}; + }, + peg$c26 = "*", + peg$c27 = peg$literalExpectation("*", false), + peg$c28 = function(innerNode) { + const CachedFunctionNode = require('./../Nodes/CachedFunctionNode'); + return new CachedFunctionNode(innerNode); + // return {type: 'cached', value:n} + }, + peg$c29 = function(label, args, values) { const LeafNode = require('./../Nodes/LeafNode'), CompositeNode = require('./../Nodes/CompositeNode'), FunctionNode = require('./../Nodes/FunctionNode'); return new FunctionNode(label, values, args); //return {'label': label, 'args': args, 'value': values}; }, - peg$c21 = function(tuple, tuples) { + peg$c30 = function(tuple, tuples) { let rs = {}; Object.assign(rs, tuple); tuples.forEach(function(t) { @@ -206,15 +226,29 @@ function peg$parse(input, options) { }); return rs; }, - peg$c22 = function(key, value) { + peg$c31 = function(key, value) { var rs = {}; rs[key] = value; return rs; }, - peg$c23 = "{}", - peg$c24 = peg$literalExpectation("{}", false), - peg$c25 = function() {return {};}, - peg$c26 = function(key0, value0, values) { + peg$c32 = "[", + peg$c33 = peg$literalExpectation("[", false), + peg$c34 = "]", + peg$c35 = peg$literalExpectation("]", false), + peg$c36 = function(el0, els) { + let res = []; + if (el0) { + res[0] = el0; + els.forEach(function(e) { + res.push(e[1]); + }); + } + return res; + }, + peg$c37 = "{}", + peg$c38 = peg$literalExpectation("{}", false), + peg$c39 = function() {return {};}, + peg$c40 = function(key0, value0, values) { let rs = {}; rs[key0] = value0; values.forEach(function(t) { @@ -222,18 +256,47 @@ function peg$parse(input, options) { }); return rs; }, - peg$c27 = /^[\-<*=>@_0-9"'a-zA-Z.]/, - peg$c28 = peg$classExpectation(["-", "<", "*", "=", ">", "@", "_", ["0", "9"], "\"", "'", ["a", "z"], ["A", "Z"], "."], false, false), - peg$c29 = function(chars) { + peg$c41 = /^[\-$!<=>@_0-9a-zA-Z.]/, + peg$c42 = peg$classExpectation(["-", "$", "!", "<", "=", ">", "@", "_", ["0", "9"], ["a", "z"], ["A", "Z"], "."], false, false), + peg$c43 = function(chars) { return chars.join(""); }, - peg$c30 = "\"", - peg$c31 = peg$literalExpectation("\"", false), - peg$c32 = /^[\-<*=>,{}@_0-9?\/:a-zA-Z.]/, - peg$c33 = peg$classExpectation(["-", "<", "*", "=", ">", ",", "{", "}", "@", "_", ["0", "9"], "?", "/", ":", ["a", "z"], ["A", "Z"], "."], false, false), - peg$c34 = function(chars) { - return '"' + chars.join("") + '"'; + peg$c44 = function(str) { + return str.slice(1,-1); + }, + peg$c45 = /^[\-.0-9]/, + peg$c46 = peg$classExpectation(["-", ".", ["0", "9"]], false, false), + peg$c47 = function(sign, chars) { + return parseFloat([sign].concat(chars).join("")); }, + peg$c48 = "\"", + peg$c49 = peg$literalExpectation("\"", false), + peg$c50 = function(chars) { return '"' +chars.join('') + '"'; }, + peg$c51 = "'", + peg$c52 = peg$literalExpectation("'", false), + peg$c53 = "\\", + peg$c54 = peg$literalExpectation("\\", false), + peg$c55 = peg$anyExpectation(), + peg$c56 = function(char) { return char; }, + peg$c57 = function(sequence) { return sequence; }, + peg$c58 = "b", + peg$c59 = peg$literalExpectation("b", false), + peg$c60 = function() { return "\b"; }, + peg$c61 = "f", + peg$c62 = peg$literalExpectation("f", false), + peg$c63 = function() { return "\f"; }, + peg$c64 = "n", + peg$c65 = peg$literalExpectation("n", false), + peg$c66 = function() { return "\n"; }, + peg$c67 = "r", + peg$c68 = peg$literalExpectation("r", false), + peg$c69 = function() { return "\r"; }, + peg$c70 = "t", + peg$c71 = peg$literalExpectation("t", false), + peg$c72 = function() { return "\t"; }, + peg$c73 = "v", + peg$c74 = peg$literalExpectation("v", false), + peg$c75 = function() { return "\x0B"; }, peg$currPos = 0, peg$savedPos = 0, @@ -477,17 +540,26 @@ function peg$parse(input, options) { function peg$parseNode() { var s0; - s0 = peg$parseRenameNode(); + s0 = peg$parseFlatObjectNode(); if (s0 === peg$FAILED) { - s0 = peg$parseOptionalNode(); + s0 = peg$parseRenameNode(); if (s0 === peg$FAILED) { - s0 = peg$parseFunctionNode(); + s0 = peg$parseOptionalNode(); if (s0 === peg$FAILED) { - s0 = peg$parseCompositeNode(); + s0 = peg$parseLogicNode(); if (s0 === peg$FAILED) { - s0 = peg$parseCastedLeafNode(); + s0 = peg$parseCachedFunctionNode(); if (s0 === peg$FAILED) { - s0 = peg$parseLeafNode(); + s0 = peg$parseFunctionNode(); + if (s0 === peg$FAILED) { + s0 = peg$parseCompositeNode(); + if (s0 === peg$FAILED) { + s0 = peg$parseCastedLeafNode(); + if (s0 === peg$FAILED) { + s0 = peg$parseLeafNode(); + } + } + } } } } @@ -561,6 +633,35 @@ function peg$parse(input, options) { return s0; } + function peg$parseFlatObjectNode() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 45) { + s1 = peg$c13; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c14); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseNode(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c15(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + function peg$parseCastedLeafNode() { var s0, s1, s2, s3, s4; @@ -568,25 +669,25 @@ function peg$parse(input, options) { s1 = peg$parseWord(); if (s1 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 40) { - s2 = peg$c13; + s2 = peg$c16; peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c14); } + if (peg$silentFails === 0) { peg$fail(peg$c17); } } if (s2 !== peg$FAILED) { s3 = peg$parseLeafNode(); if (s3 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 41) { - s4 = peg$c15; + s4 = peg$c18; peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c16); } + if (peg$silentFails === 0) { peg$fail(peg$c19); } } if (s4 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c17(s1, s3); + s1 = peg$c20(s1, s3); s0 = s1; } else { peg$currPos = s0; @@ -615,7 +716,7 @@ function peg$parse(input, options) { s1 = peg$parseWord(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c18(s1); + s1 = peg$c21(s1); } s0 = s1; @@ -631,7 +732,74 @@ function peg$parse(input, options) { s2 = peg$parseComplex(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c19(s1, s2); + s1 = peg$c22(s1, s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseLogicNode() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 64) { + s1 = peg$c23; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c24); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseFunctionNode(); + if (s2 !== peg$FAILED) { + s3 = peg$parseLogicNode(); + if (s3 === peg$FAILED) { + s3 = null; + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c25(s2, s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseCachedFunctionNode() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 42) { + s1 = peg$c26; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c27); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseFunctionNode(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c28(s2); s0 = s1; } else { peg$currPos = s0; @@ -659,7 +827,7 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c20(s1, s2, s3); + s1 = peg$c29(s1, s2, s3); s0 = s1; } else { peg$currPos = s0; @@ -682,11 +850,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 40) { - s1 = peg$c13; + s1 = peg$c16; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c14); } + if (peg$silentFails === 0) { peg$fail(peg$c17); } } if (s1 !== peg$FAILED) { s2 = peg$parseKVTuple(); @@ -742,15 +910,15 @@ function peg$parse(input, options) { } if (s3 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 41) { - s4 = peg$c15; + s4 = peg$c18; peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c16); } + if (peg$silentFails === 0) { peg$fail(peg$c19); } } if (s4 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c21(s2, s3); + s1 = peg$c30(s2, s3); s0 = s1; } else { peg$currPos = s0; @@ -789,7 +957,7 @@ function peg$parse(input, options) { s3 = peg$parseKVValue(); if (s3 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c22(s1, s3); + s1 = peg$c31(s1, s3); s0 = s1; } else { peg$currPos = s0; @@ -810,12 +978,113 @@ function peg$parse(input, options) { function peg$parseKVValue() { var s0; - s0 = peg$parseValueWord(); + s0 = peg$parseNumber(); if (s0 === peg$FAILED) { - s0 = peg$parseWord(); + s0 = peg$parseStringLiteralValue(); if (s0 === peg$FAILED) { - s0 = peg$parseKVCompValue(); + s0 = peg$parseWord(); + if (s0 === peg$FAILED) { + s0 = peg$parseKVCompValue(); + if (s0 === peg$FAILED) { + s0 = peg$parseKVArray(); + } + } + } + } + + return s0; + } + + function peg$parseKVArray() { + var s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 91) { + s1 = peg$c32; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c33); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseKVValue(); + if (s2 === peg$FAILED) { + s2 = null; } + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parseKVValue(); + if (s6 !== peg$FAILED) { + s5 = [s5, s6]; + s4 = s5; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c2; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c3); } + } + if (s5 !== peg$FAILED) { + s6 = peg$parseKVValue(); + if (s6 !== peg$FAILED) { + s5 = [s5, s6]; + s4 = s5; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + } + if (s3 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 93) { + s4 = peg$c34; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c35); } + } + if (s4 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c36(s2, s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; } return s0; @@ -825,16 +1094,16 @@ function peg$parse(input, options) { var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10; s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c23) { - s1 = peg$c23; + if (input.substr(peg$currPos, 2) === peg$c37) { + s1 = peg$c37; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c24); } + if (peg$silentFails === 0) { peg$fail(peg$c38); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c25(); + s1 = peg$c39(); } s0 = s1; if (s0 === peg$FAILED) { @@ -951,7 +1220,7 @@ function peg$parse(input, options) { } if (s6 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c26(s2, s4, s5); + s1 = peg$c40(s2, s4, s5); s0 = s1; } else { peg$currPos = s0; @@ -987,22 +1256,22 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = []; - if (peg$c27.test(input.charAt(peg$currPos))) { + if (peg$c41.test(input.charAt(peg$currPos))) { s2 = input.charAt(peg$currPos); peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c28); } + if (peg$silentFails === 0) { peg$fail(peg$c42); } } if (s2 !== peg$FAILED) { while (s2 !== peg$FAILED) { s1.push(s2); - if (peg$c27.test(input.charAt(peg$currPos))) { + if (peg$c41.test(input.charAt(peg$currPos))) { s2 = input.charAt(peg$currPos); peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c28); } + if (peg$silentFails === 0) { peg$fail(peg$c42); } } } } else { @@ -1010,58 +1279,104 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c29(s1); + s1 = peg$c43(s1); } s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseStringLiteralValue(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c44(s1); + } + s0 = s1; + } return s0; } - function peg$parseValueWord() { + function peg$parseNumber() { var s0, s1, s2, s3; s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 34) { - s1 = peg$c30; + if (input.charCodeAt(peg$currPos) === 45) { + s1 = peg$c13; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c31); } + if (peg$silentFails === 0) { peg$fail(peg$c14); } + } + if (s1 === peg$FAILED) { + s1 = null; } if (s1 !== peg$FAILED) { s2 = []; - if (peg$c32.test(input.charAt(peg$currPos))) { + if (peg$c45.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c33); } + if (peg$silentFails === 0) { peg$fail(peg$c46); } } if (s3 !== peg$FAILED) { while (s3 !== peg$FAILED) { s2.push(s3); - if (peg$c32.test(input.charAt(peg$currPos))) { + if (peg$c45.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c33); } + if (peg$silentFails === 0) { peg$fail(peg$c46); } } } } else { s2 = peg$FAILED; } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c47(s1, s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseStringLiteralValue() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 34) { + s1 = peg$c48; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c49); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseDoubleStringCharacter(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseDoubleStringCharacter(); + } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s3 = peg$c30; + s3 = peg$c48; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c31); } + if (peg$silentFails === 0) { peg$fail(peg$c49); } } if (s3 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c34(s2); + s1 = peg$c50(s2); s0 = s1; } else { peg$currPos = s0; @@ -1075,6 +1390,323 @@ function peg$parse(input, options) { peg$currPos = s0; s0 = peg$FAILED; } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 39) { + s1 = peg$c51; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c52); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseSingleStringCharacter(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseSingleStringCharacter(); + } + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 39) { + s3 = peg$c51; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c52); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c50(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + function peg$parseDoubleStringCharacter() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + if (input.charCodeAt(peg$currPos) === 34) { + s2 = peg$c48; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c49); } + } + if (s2 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 92) { + s2 = peg$c53; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c54); } + } + } + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = void 0; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c55); } + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c56(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c53; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c54); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseEscapeSequence(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c57(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + function peg$parseSingleStringCharacter() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + if (input.charCodeAt(peg$currPos) === 39) { + s2 = peg$c51; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c52); } + } + if (s2 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 92) { + s2 = peg$c53; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c54); } + } + } + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = void 0; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c55); } + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c56(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c53; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c54); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseEscapeSequence(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c57(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + function peg$parseEscapeSequence() { + var s0, s1; + + if (input.charCodeAt(peg$currPos) === 39) { + s0 = peg$c51; + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c52); } + } + if (s0 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 34) { + s0 = peg$c48; + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c49); } + } + if (s0 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 92) { + s0 = peg$c53; + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c54); } + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 98) { + s1 = peg$c58; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c59); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c60(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 102) { + s1 = peg$c61; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c62); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c63(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 110) { + s1 = peg$c64; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c65); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c66(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 114) { + s1 = peg$c67; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c68); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c69(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 116) { + s1 = peg$c70; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c71); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c72(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 118) { + s1 = peg$c73; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c74); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c75(); + } + s0 = s1; + } + } + } + } + } + } + } + } return s0; } diff --git a/src/Parser/grammer.pegjs b/src/Parser/grammer.pegjs index 3ae2557..d3ee592 100644 --- a/src/Parser/grammer.pegjs +++ b/src/Parser/grammer.pegjs @@ -17,8 +17,11 @@ Complex = "{" firstNode:Node? nodes:("," Node)* "}" { } Node - = RenameNode + = FlatObjectNode + / RenameNode / OptionalNode + / LogicNode + / CachedFunctionNode / FunctionNode / CompositeNode / CastedLeafNode @@ -34,6 +37,11 @@ OptionalNode = "?" innerNode:Node { return new OptionalNode(innerNode); } +FlatObjectNode = "-" innerNode:Node { + const FlatObjectNode = require('./../Nodes/FlatObjectNode') + return new FlatObjectNode(innerNode); +} + CastedLeafNode = cast:Word "(" innerNode:LeafNode ")" { const CastedLeafNode = require('./../Nodes/CastedLeafNode'); return new CastedLeafNode(cast, innerNode); @@ -55,6 +63,18 @@ CompositeNode = label:Word values:Complex { //return {'label' : label, 'value': values}; } +LogicNode = "@" n:FunctionNode f:LogicNode?{ + const LogicNode = require('./../Nodes/LogicNode'); + return new LogicNode(n.getName(), n.children, n.args, f); + //return {'t':'logic', 'l':n.label, 'a':n.args}; +} + +CachedFunctionNode = "*" innerNode:FunctionNode { + const CachedFunctionNode = require('./../Nodes/CachedFunctionNode'); + return new CachedFunctionNode(innerNode); + // return {type: 'cached', value:n} +} + FunctionNode = label:Word args:ArgSet values:Complex? { const LeafNode = require('./../Nodes/LeafNode'), CompositeNode = require('./../Nodes/CompositeNode'), @@ -80,7 +100,19 @@ KVTuple = key:Word ":" value:KVValue { } //Added new intermidiate type to support (key:{subkey:value}) -KVValue = ValueWord / Word / KVCompValue +KVValue = Number / ValueWord / Word / KVCompValue / KVArray + +//Support array parameters +KVArray = "[" el0:KVValue? els:("," value:KVValue)* "]" { + let res = []; + if (el0) { + res[0] = el0; + els.forEach(function(e) { + res.push(e[1]); + }); + } + return res; +} //This support having json types in args @@ -94,10 +126,37 @@ KVCompValue = "{}" {return {};} //empty return rs; } -Word = chars:[-<*=>@_0-9"'a-zA-Z.]+ { +Word = chars:[-$!<=>@_0-9a-zA-Z.]+ { return chars.join(""); +} / str:StringLiteralValue { + return str.slice(1,-1); +} + +ValueWord = StringLiteralValue + +Number = sign:"-"? chars:[-.0-9]+ { + return parseFloat([sign].concat(chars).join("")); } -ValueWord = '"' chars:[-<*=>,{}@_0-9\?/:a-zA-Z.]+ '"' { - return '"' + chars.join("") + '"'; -} \ No newline at end of file +StringLiteralValue + = '"' chars:DoubleStringCharacter* '"' { return '"' +chars.join('') + '"'; } + / "'" chars:SingleStringCharacter* "'" { return '"' +chars.join('') + '"'; } + +DoubleStringCharacter + = !('"' / "\\") char:. { return char; } + / "\\" sequence:EscapeSequence { return sequence; } + +SingleStringCharacter + = !("'" / "\\") char:. { return char; } + / "\\" sequence:EscapeSequence { return sequence; } + +EscapeSequence + = "'" + / '"' + / "\\" + / "b" { return "\b"; } + / "f" { return "\f"; } + / "n" { return "\n"; } + / "r" { return "\r"; } + / "t" { return "\t"; } + / "v" { return "\x0B"; } \ No newline at end of file diff --git a/src/RQL.js b/src/RQL.js index fb456bf..567ed17 100644 --- a/src/RQL.js +++ b/src/RQL.js @@ -6,13 +6,26 @@ const parser = require('./Parser/Parser'), RQLQuery = require('./RQLQuery'); + +const BASE_CONTEXT = { + "false": false, + "true": true +}; + class RQL { constructor(ops) { this.ops = ops; + this.ops.logger = { + log(msg) { + if (ops.logLevel === 'info') + console.log(msg); + } + }; } query(queryString, context) { if(!context) context = {}; + context = Object.assign(context, BASE_CONTEXT); return new Promise((resolve, reject) => { parser.parse(queryString) .catch(reject) diff --git a/src/RQLQuery.js b/src/RQLQuery.js index bae675f..debabf5 100644 --- a/src/RQLQuery.js +++ b/src/RQLQuery.js @@ -3,6 +3,8 @@ */ "use strict"; +const ObjectNode = require('./Nodes/ObjectNode'); + class WQLQuery { /** @@ -21,26 +23,9 @@ class WQLQuery { * @param context the context to perform the query in * @return Promise */ - eval(context) { - let res = {}; - let promises = []; - this.roots.forEach((root) => { - promises.push(new Promise((resolve, reject) => { - root.eval(context, this.options) - .then((val) => { - res[root.getName()] = val; - resolve(); - }) - .catch(reject); - })); - }); - return new Promise((resolve, reject) => { - Promise.all(promises) - .then(()=> { - resolve(res); - }) - .catch(reject); - }); + async eval(context) { + const queryNode = new ObjectNode('root', this.roots); + return await queryNode.eval(context, this.options); } } diff --git a/test/E2E.js b/test/E2E.js index f35c58a..22480f3 100644 --- a/test/E2E.js +++ b/test/E2E.js @@ -24,23 +24,24 @@ const rql = new RapidQL({ const assert = require("assert"); -describe('E2E API Queries', () => { - it('should run', async () => { - let r = await rql.query(` - { - res:RapidAPI.NasaAPI.getPictureOfTheDay() { - explanation, - title, - pic_url:url, - ? randomFieldThatShouldBeNull - } - } - `, {}); - assert.equal(r.hasOwnProperty('RapidAPI.NasaAPI.getPictureOfTheDay'), false); - assert.equal(r.hasOwnProperty('res'), true); - assert.equal(r.res.hasOwnProperty('explanation'), true); - assert.equal(r.res.hasOwnProperty('title'), true); - assert.equal(r.res.hasOwnProperty('pic_url'), true); - assert.equal(r.res.randomFieldThatShouldBeNull, null); - }); -}); \ No newline at end of file +// describe('E2E API Queries', function () { +// this.timeout(10000); +// it('should run', async () => { +// let r = await rql.query(` +// { +// res:RapidAPI.NasaAPI.getPictureOfTheDay() { +// explanation, +// title, +// pic_url:url, +// ? randomFieldThatShouldBeNull +// } +// } +// `, {}); +// assert.equal(r.hasOwnProperty('RapidAPI.NasaAPI.getPictureOfTheDay'), false); +// assert.equal(r.hasOwnProperty('res'), true); +// assert.equal(r.res.hasOwnProperty('explanation'), true); +// assert.equal(r.res.hasOwnProperty('title'), true); +// assert.equal(r.res.hasOwnProperty('pic_url'), true); +// assert.equal(r.res.randomFieldThatShouldBeNull, null); +// }); +// }); \ No newline at end of file diff --git a/test/array-node.js b/test/array-node.js new file mode 100644 index 0000000..47d1d0f --- /dev/null +++ b/test/array-node.js @@ -0,0 +1,76 @@ +/** + * Created by iddo on 7/28/17. + */ + +const {assert} = require('chai'); +const { describe, it } = require('mocha'); +const ArrayNode = require('../src/Nodes/ArrayNode'); +const LeafNode = require('../src/Nodes/LeafNode'); + +describe("ArrayNode", () => { + it('should throw an error if object is not an array', (done) => { + let n = new ArrayNode('arr', []); + n.eval({arr: "asd"}, {}) + .then(() => { + done('Should have thrown an error'); + }) + .catch(() => { + done(); + }); + }); + + it('should throw an error if object is not in context', (done) => { + let n = new ArrayNode('arr', []); + n.eval({}, {}) + .then(() => { + done('Should have thrown an error'); + }) + .catch(() => { + done(); + }); + }); + + it('should support object array', async () => { + let n = new ArrayNode('arr', [ + new LeafNode('a') + ]); + let arr = [{a:1}, {a:2}, {a:3}]; + let res = await n.eval({arr:arr}, {}); + assert.deepEqual(res, arr); + }); + + it('should support referencing properties from outside context in object array', async () => { + /* + { + arr { + a, + b + } + } + */ + let n = new ArrayNode('arr', [ + new LeafNode('a'), + new LeafNode('b') + ]); + let arrIn = [{a:1}, {a:2}, {a:3}]; + let arrComp = [{a:1, b: "b"}, {a:2, b: "b"}, {a:3, b: "b"}]; + let res = await n.eval({arr:arrIn, b: "b"}, {}); + assert.deepEqual(res, arrComp); + }); + + it('should support string array - explicit', async () => { + let n = new ArrayNode('arr', [ + new LeafNode('arr') + ]); + let arr = ["aa", "bb", "cc"]; + let res = await n.eval({arr:arr}, {}); + assert.deepEqual(res, [{arr:'aa'},{arr:'bb'},{arr:'cc'}]); + }); + + it('should support string array - implicit', async () => { + let n = new ArrayNode('arr', []); + let arr = ["aa", "bb", "cc"]; + let res = await n.eval({arr:arr}, {}); + assert.deepEqual(res, arr); + }); +}); \ No newline at end of file diff --git a/test/composite-node.js b/test/composite-node.js new file mode 100644 index 0000000..ab693ac --- /dev/null +++ b/test/composite-node.js @@ -0,0 +1,66 @@ +/** + * Created by iddo on 7/28/17. + */ + +const {assert} = require('chai'); +const { describe, it } = require('mocha'); +const ArrayNode = require('../src/Nodes/ArrayNode'); +const CompositeNode = require('../src/Nodes/CompositeNode'); +const LeafNode = require('../src/Nodes/LeafNode'); + +describe("CompositeNode", () => { + it('should throw an error if object is not an array / object', (done) => { + let n = new CompositeNode('arr', []); + n.eval({arr: "asd"}, {}) + .then(() => { + done('Should have thrown an error'); + }) + .catch((err) => { + done(); + }); + }); + + it('should throw an error if object is not in context', (done) => { + let n = new CompositeNode('arr', []); + n.eval({}, {}) + .then(() => { + done('Should have thrown an error'); + }) + .catch((err) => { + done(); + }); + }); + + it('should materialize into array - implicit', async () => { + let n = new CompositeNode('arr', []); + let arr = ["aa", "bb", "cc"]; + let res = await n.eval({arr:arr}, {}); + assert.deepEqual(res, arr); + }); + + it('should materialize into array - explicit', async () => { + let n = new CompositeNode('arr', [ + new LeafNode('a') + ]); + let arr = [{a:1}, {a:2}, {a:3}]; + let res = await n.eval({arr:arr}, {}); + assert.deepEqual(res, arr); + }); + + it('should materialize into object - implicit', async () => { + let n = new CompositeNode('obj', []); + let obj = {a:'aa', b:'bb'}; + let res = await n.eval({obj: obj}, {}); + assert.deepEqual(res, obj); + }); + + it('should materialize into object - implicit', async () => { + let n = new CompositeNode('obj', [ + new LeafNode('a'), + new LeafNode('b') + ]); + let obj = {a:'aa', b:'bb'}; + let res = await n.eval({obj: obj}, {}); + assert.deepEqual(res, obj); + }); +}); \ No newline at end of file diff --git a/test/driverTests.js b/test/driverTests.js index 1c97f47..b295427 100644 --- a/test/driverTests.js +++ b/test/driverTests.js @@ -12,4 +12,18 @@ describe('PostgreSQL Driver', ()=> { require('./../src/Nodes/FunctionNodes/PostgreSQLDriver/test/utils')(); require('./../src/Nodes/FunctionNodes/PostgreSQLDriver/test/insertGenerator')(); require('./../src/Nodes/FunctionNodes/PostgreSQLDriver/test/updateGenerator')(); + require('./../src/Nodes/FunctionNodes/MongoDBDriver/test/utils')(); +}); + +describe('HTTP Driver', () => { + require('./../src/Nodes/FunctionNodes/HttpDriver/test/http')(); +}); + +describe('Map Reduce', () => { + require('./../src/Nodes/FunctionNodes/MapReduce/test/mapReduce')(); + require('./../src/Nodes/FunctionNodes/MapReduce/test/MapReduceNode')(); +}); + +describe('CSV', () => { + require('./../src/Nodes/FunctionNodes/CsvDriver/test/csv')(); }); \ No newline at end of file diff --git a/test/flat-object-node.js b/test/flat-object-node.js new file mode 100644 index 0000000..ee1a71a --- /dev/null +++ b/test/flat-object-node.js @@ -0,0 +1,71 @@ +/** + * Created by iddo on 4/19/18. + */ +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const { expect, assert } = chai; +const { describe, it } = require('mocha'); +const ObjectNode = require('../src/Nodes/ObjectNode'); +const FlatObjectNode = require('../src/Nodes/FlatObjectNode'); +const LeafNode = require('../src/Nodes/LeafNode'); + +describe('ObjectNode', () => { + + it('should handle valid objects properly', async () => { + let n = new FlatObjectNode (new ObjectNode('obj', [ + new LeafNode('a'), + new LeafNode('b') + ])); + + let v = await n.eval({ + obj: { + a: 'AA', + b: 'BB' + } + }); + + assert.equal(v.a, 'AA'); + assert.equal(v.b, 'BB'); + }); + + it('should support referencing properties from outside object within object', async () => { + let n = new FlatObjectNode (new ObjectNode('obj', [ + new LeafNode('a'), + new LeafNode('b') + ])); + + let v = await n.eval({ + obj: { + a: 'AA' + }, + b: 'BB' + }); + + assert.equal(v.a, 'AA'); + assert.equal(v.b, 'BB'); + }); + + it('should be flat (aka show as properties vs as object) when placed in Object Node', async () => { + let n = new ObjectNode('root', [ + new FlatObjectNode (new ObjectNode('obj', [ + new LeafNode('a'), + new LeafNode('b') + ])) + ]); + + let v = await n.eval({ + root: { + obj: { + a: 'AA', + b: 'BB' + } + } + }); + + assert.deepEqual(v, { + a: 'AA', + b: 'BB' + }); + }); +}); + diff --git a/test/functionNode.js b/test/functionNode.js index df4a422..45106cf 100644 --- a/test/functionNode.js +++ b/test/functionNode.js @@ -7,20 +7,6 @@ const assert = require('assert'), FunctionNode = require('../src/Nodes/FunctionNode'); describe('Function Nodes', () => { - describe('getFromContext', () => { - it('should get the correct value from context', () => { - assert.deepEqual(FunctionNode.getFromContext('a', {'a': 'HELLO'}), "HELLO"); - }); - - it('should throw an error if key does not exist in context', (done) => { - try { - FunctionNode.getFromContext('a', {'b':'BBB'}); - done("Didn't throw error for key that is not in context"); - } catch (e) { - done(); - } - }); - }); describe('quoted', () => { it('should identify a string quoted with double quotes', ()=> { @@ -53,15 +39,28 @@ describe('Function Nodes', () => { it('shouldnt change string with no quotes', () => { assert.deepEqual(FunctionNode.removeQuotes("asdasda"), 'asdasda'); }); + + it('should ignore single quote', () => { + assert.deepEqual(FunctionNode.removeQuotes("'asdasda"), '\'asdasda'); + assert.deepEqual(FunctionNode.removeQuotes("\"asdasda"), '\"asdasda'); + assert.deepEqual(FunctionNode.removeQuotes("asdasda'"), 'asdasda\''); + assert.deepEqual(FunctionNode.removeQuotes("asdasda\""), 'asdasda\"'); + assert.deepEqual(FunctionNode.removeQuotes("asdasda\"a"), 'asdasda\"a'); + assert.deepEqual(FunctionNode.removeQuotes("asdasda\'a"), 'asdasda\'a'); + }); }); describe('Recursive replace', () => { - it('should replace a single constant', () => { + it('should replace a single constant string', () => { assert.deepEqual(FunctionNode.recursiveReplace({a: '"b"'}, {}), {a:'b'}); }); it('should replace multiple constants', ()=> { - assert.deepEqual(FunctionNode.recursiveReplace({a: '"b"', c: '"bbb"'}, {}), {a:'b', c:'bbb'}); + assert.deepEqual(FunctionNode.recursiveReplace({a: '"b"', c: '"bbb"', num:-5}, {}), {a:'b', c:'bbb', num:-5}); + }); + + it('should replace a single constant number', () => { + assert.deepEqual(FunctionNode.recursiveReplace({a: 123}, {}), {a:123}); }); it('should replace variables with context value', () => { @@ -75,5 +74,24 @@ describe('Function Nodes', () => { it('should traverse down objects, replacing from the context tree', () => { assert.deepEqual(FunctionNode.recursiveReplace({b:{a:'a'}}, {a:2}), {b:{a:2}}); }); + + it('should replace template (mustache templates)', () => { + assert.deepEqual(FunctionNode.recursiveReplace({a:'"a={{a}}"'}, {a:2}), {a:"a=2"}); + assert.deepEqual(FunctionNode.recursiveReplace({a:'"{{a}}"'}, {a:2}), {a:"2"}); + }); + + it('should handle deep referencing (dot notation) in objects', () => { + assert.deepEqual(FunctionNode.recursiveReplace({a:'a.b'}, {a:{b:2}}), {a:2}); + }); + + describe('arrays', () => { + it ('should support array of constants', () => { + assert.deepEqual(FunctionNode.recursiveReplace({arr:['"a"','"b"', 5]}, {}), {arr:['a','b', 5]}); + }); + + it ('should support array of keys', () => { + assert.deepEqual(FunctionNode.recursiveReplace({arr:['a','b']}, {a: 1, b: "bbb"}), {arr:[1,'bbb']}); + }); + }); }); }); \ No newline at end of file diff --git a/test/generative/gen-parser.js b/test/generative/gen-parser.js new file mode 100644 index 0000000..70bf040 --- /dev/null +++ b/test/generative/gen-parser.js @@ -0,0 +1,100 @@ +/** + * Created by iddo on 7/27/17. + */ +"use strict"; + +const { describe, it } = require('mocha'); +const { check, gen } = require('mocha-testcheck'); +const { expect, assert } = require('chai'); +require('mocha-testcheck').install(); + +const LeafNode = require('../../src/Nodes/LeafNode'), + CompositeNode = require('../../src/Nodes/CompositeNode'), + RenameNode = require('../../src/Nodes/RenameNode'), + OptionalNode = require('../../src/Nodes/OptionalNode'), + CastedLeafNode = require('../../src/Nodes/CastedLeafNode'), + FunctionNode = require('../../src/Nodes/FunctionNode'); + +const parse = require('../../src/Parser/Parser').parse; + +describe('Generative - Parser', () => { + describe('Leaf Nodes', () => { + check.it('should detect simple alpha-numeric leaf nodes that are un-quoted',{result: true, times: 50}, gen.array(gen.alphaNumString), async (leafs) => { + // Filter out empty strings and remove spaces + leafs = leafs.map(a => a.replace(" ", "")).filter(a => a.length > 0); + + const queryString = `{ + ${leafs.join(",\n")} + }`; + const val = await parse(queryString); + + assert.equal(val.length, leafs.length); + + for (let i = 0; i < leafs.length; i++) { + assert.equal(leafs[i], val[i].getName()); + } + }); + + check.it('should detect double-quoted freestyle ascii leaf nodes',{result: true, times: 50}, gen.array(gen.asciiString), async (leafs) => { + leafs = leafs.map(a => a.replace(" ", "")); + leafs = leafs.map(a => a.replace(/"/g, '')); + leafs = leafs.map(a => a.replace(/\\/g, '')); + // Filter out empty strings + leafs = leafs.filter(a => a.length > 0); + + const queryString = `{ + ${leafs.map(l=> '"'+l+'"').join(",\n")} + }`; + const val = await parse(queryString); + + assert.equal(val.length, leafs.length); + + for (let i = 0; i < leafs.length; i++) { + assert.equal(leafs[i], val[i].getName()); + } + }); + + check.it('should detect single-quoted freestyle ascii leaf nodes',{result: true, times: 50}, gen.array(gen.asciiString), async (leafs) => { + leafs = leafs.map(a => a.replace(/'/g, '')); + leafs = leafs.map(a => a.replace(/\\/g, '')); + leafs = leafs.map(a => a.replace(/ /g, '')); + leafs = leafs.map(a => a.replace(/"/g, '')); + // Filter out empty strings + leafs = leafs.filter(a => a.length > 0); + + const queryString = `{ + ${leafs.map(l=> '\''+l+'\'').join(",\n")} + }`; + const val = await parse(queryString); + + assert.equal(val.length, leafs.length); + + for (let i = 0; i < leafs.length; i++) { + assert.equal(leafs[i], val[i].getName()); + } + }); + }); + + describe('Function Nodes', () => { + check.it('accepts string parameters', {result: true, times: 50}, gen.asciiString, gen.asciiString, gen.alphaNumString, gen.alphaNumString, async (param1, param2, key1, key2) => { + + param1 = param1.replace(/'/g, '').replace(/\\/g, '').replace(/"/g, ''); + param2 = param2.replace(/'/g, '').replace(/\\/g, '').replace(/"/g, ''); + + //make sure keys aren't empty + key1 += (key1.length === 0) ? "a" : ""; + key2 += (key2.length === 0) ? "b" : ""; + + const queryString = `{ + RapidAPI.Name.Function(${key1}:"${param1}", ${key2}:"${param2}") { + a + } + }`; + const val = await parse(queryString); + assert.equal(val.length, 1); // Exactly 1 root node + assert.equal(val[0].hasOwnProperty('args'), true); // Check type. Only function nodes have args (it can be sub-type) + assert.equal(val[0].args[key1], `"${param1}"`); //Check simple arg + assert.equal(val[0].args[key2], `"${param2}"`); //Check simple arg + }); + }); +}); \ No newline at end of file diff --git a/test/generative/gen-typecasted-leaf-nodes.js b/test/generative/gen-typecasted-leaf-nodes.js new file mode 100644 index 0000000..824af2e --- /dev/null +++ b/test/generative/gen-typecasted-leaf-nodes.js @@ -0,0 +1,48 @@ +/** + * Created by iddo on 7/27/17. + */ +"use strict"; + +const { describe, it } = require('mocha'); +const { check, gen } = require('mocha-testcheck'); +const { expect, assert } = require('chai'); +require('mocha-testcheck').install(); + +const LeafNode = require('../../src/Nodes/LeafNode'), + CompositeNode = require('../../src/Nodes/CompositeNode'), + RenameNode = require('../../src/Nodes/RenameNode'), + OptionalNode = require('../../src/Nodes/OptionalNode'), + CastedLeafNode = require('../../src/Nodes/CastedLeafNode'), + FunctionNode = require('../../src/Nodes/FunctionNode'); + +const parse = require('../../src/Parser/Parser').parse; + +describe('Generative - CastedLeafNode.typeConverters', () => { + describe('int', () => { + const converter = CastedLeafNode.typeConverters.int; + + check.it('should return ints as ints', gen.int, (i) => { + assert.equal(converter(i),i); + }); + + check.it('should return floats as ints', gen.numberWithin(-100000, 100000), (i) => { + assert.equal(converter(i), parseInt(i)); + }); + + check.it('should parse strings as ints', gen.int, (i) => { + assert.equal(converter(`${i}`),i); + }); + }); + + describe('float', () => { + const converter = CastedLeafNode.typeConverters.float; + + check.it('should return floats as floats', gen.numberWithin(-100000, 100000), (i) => { + assert.equal(converter(i),i); + }); + + check.it('should parse strings as floats', gen.numberWithin(-100000, 100000), (i) => { + assert.equal(converter(`${i}`),i); + }); + }); +}); \ No newline at end of file diff --git a/test/leaf-node.js b/test/leaf-node.js index a918471..ba5da71 100644 --- a/test/leaf-node.js +++ b/test/leaf-node.js @@ -29,9 +29,33 @@ describe("LeafNode", () => { ln.eval({}).then((val) => { //Running on empty context done("Should not resolve"); }).catch((err) => { - assert(""+err, "Name a does not exist in context [object Object]"); + assert(""+err, "Name a does not exist in context {}"); done(); }); }); + + it('should return deep value from object', (done) => { + (new LeafNode('a.b')).eval({ + a : { + b : 12 + } + }).then((val) => { + assert.equal(val, 12); + done(); + }).catch((err) => { + done(`Failed with error: ${err}`); + }); + }); + + it('should resolve path with points directly when possible', (done) => { + (new LeafNode('a.b')).eval({ + "a.b": 12 + }).then((val) => { + assert.equal(val, 12); + done(); + }).catch((err) => { + done(`Failed with error: ${err}`); + }); + }); }); }); \ No newline at end of file diff --git a/test/logic-node.js b/test/logic-node.js new file mode 100644 index 0000000..7b3541c --- /dev/null +++ b/test/logic-node.js @@ -0,0 +1,291 @@ +/** + * Created by iddo on 9/25/18. + */ +"use strict"; + +const {assert} = require('chai'); +const { describe, it } = require('mocha'); +const LeafNode = require("../src/Nodes/LeafNode"); +const LogicNode = require("../src/Nodes/LogicNode"); + +describe("LogicNode", () => { + let _context = { + a: "aaa", + b: "bbb", + c: 10, + d: 5, + obj: { + aaa: "aaa" + } + }; + + describe ("__if logic", () => { + const __if = LogicNode.logicFunctions.if; + const prettyIf = (val1, val2, comparison) => __if({val1,val2,comparison}); + + describe("==", () => { + it ("should use comparison by default", () => { + assert.isOk(prettyIf("aa","aa")); + assert.isNotOk(prettyIf("aa","bb")); + }); + + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf("aa","aa", "==")); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf("aa","abba", "==")); + }); + }); + + describe("!=", () => { + let comp = "!="; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf("aa","bb", comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf("aa","aa", comp)); + }); + }); + + describe(">", () => { + let comp = ">"; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf(5,3, comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf(4,83, comp)); + }); + }); + + describe(">=", () => { + let comp = ">="; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf(5,3, comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf(4,83, comp)); + }); + }); + + describe("<", () => { + let comp = "<"; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf(3,35, comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf(48,8, comp)); + }); + }); + + describe("<=", () => { + let comp = "<="; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf(3,35, comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf(48,8, comp)); + }); + }); + + describe("prefixes", () => { + let comp = "prefixes"; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf("+1","+14158496404", comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf("+972","+14158496404", comp)); + }); + }); + + describe("in", () => { + let comp = "in"; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf("+1",["+1","+2","+3"], comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf("+4",["+1","+2","+3"], comp)); + }); + }); + + describe("notIn", () => { + let comp = "notIn"; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf("+4",["+1","+2","+3"], comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf("+1",["+1","+2","+3"], comp)); + }); + }); + + describe("prefixIn", () => { + let comp = "prefixIn"; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf("+14158496404",["+1","+2","+3"], comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf("+972544266822",["+1","+2","+3"], comp)); + }); + }); + + describe("prefixNotIn", () => { + let comp = "prefixNotIn"; + it ("should return true on positive comparison", () => { + assert.isOk(prettyIf("+972544266822",["+1","+2","+3"], comp)); + }); + + it ("should return false on negative comparison", () => { + assert.isNotOk(prettyIf("+14158496404",["+1","+2","+3"], comp)); + }); + }); + }); + + describe("general behaviour", () => { + + + it ("should return null when condition is false", async () => { + let ln = new LogicNode("if.", [ + new LeafNode('a') + ], { + comparison: '"=="', + val1: '"aaa"', + val2: '"bbb"' + }); + + let res = await ln.eval(_context, {}); + assert.deepEqual(res, null); + }); + + it ("should return inner object when evaluation is true", async () => { + let ln = new LogicNode("if.", [ + new LeafNode('a') + ], { + comparison: '"=="', + val1: '"aaa"', + val2: '"aaa"' + }); + + let res = await ln.eval(_context, {}); + assert.deepEqual(res, {a: "aaa"}); + }); + + it ("should use variable data for comparison as well as literals", async () => { + let ln = new LogicNode("if.", [ + new LeafNode('a') + ], { + comparison: '"=="', + val1: '"aaa"', + val2: 'a' + }); + + let res = await ln.eval(_context, {}); + assert.deepEqual(res, {a: "aaa"}); + }); + + it ("should support variable data from deep object for comparison", async () => { + let ln = new LogicNode("if.", [ + new LeafNode('a') + ], { + comparison: '"=="', + val1: 'obj.aaa', + val2: 'a' + }); + + let res = await ln.eval(_context, {}); + assert.deepEqual(res, {a: "aaa"}); + }); + + }); + + describe("follow on nodes (elseif, else)", () => { + it ("should not run @else node if @if node is true", async () => { + let ln = new LogicNode("if.", [ + new LeafNode('a') + ], { + comparison: '"=="', + val1: '"aaa"', + val2: '"aaa"' + }, new LogicNode( "else.", [ + new LeafNode('b') + ], { + + })); + + let res = await ln.eval(_context, {}); + assert.deepEqual(res, {a: "aaa"}); + }); + + it ("should default to @else node if @if node is false", async () => { + let ln = new LogicNode("if.", [ + new LeafNode('a') + ], { + comparison: '"=="', + val1: '"aaa"', + val2: '"bbb"' + }, new LogicNode( "else.", [ + new LeafNode('b') + ], { + + })); + + let res = await ln.eval(_context, {}); + assert.deepEqual(res, {b: "bbb"}); + }); + + it ("should default to @elseif node if @if node is false and @elseif is true", async () => { + let ln = new LogicNode("if.", [ + new LeafNode('a') + ], { + comparison: '"=="', + val1: '"aaa"', + val2: '"bbb"' + }, new LogicNode( "elseif.", [ + new LeafNode('b') + ], { + comparison: '"=="', + val1: '"aaa"', + val2: '"aaa"' + }, new LogicNode( "else.", [ + new LeafNode('c') + ], { + + }))); + + let res = await ln.eval(_context, {}); + assert.deepEqual(res, {b: "bbb"}); + }); + + it ("should default to @else node if @if node is false and @else is false", async () => { + let ln = new LogicNode("if.", [ + new LeafNode('a') + ], { + comparison: '"=="', + val1: '"aaa"', + val2: '"bbb"' + }, new LogicNode( "elseif.", [ + new LeafNode('b') + ], { + comparison: '"=="', + val1: '"aaa"', + val2: '"bbb"' + }, new LogicNode( "else.", [ + new LeafNode('c') + ], { + + }))); + + let res = await ln.eval(_context, {}); + assert.deepEqual(res, {c: 10}); + }); + }); + +}); \ No newline at end of file diff --git a/test/node-interface.js b/test/node-interface.js index 200bb12..45ed883 100644 --- a/test/node-interface.js +++ b/test/node-interface.js @@ -40,7 +40,7 @@ describe(`Node interfaces`, () => { }); it(`should return a promise when called`, () => { - let p = node.eval({}, {}); + let p = node.eval({"RapidAPI.a":["ad"]}, {RapidAPI:{}}); assert.equal(true, (Promise.resolve(p) == p)); //http://stackoverflow.com/questions/27746304/how-do-i-tell-if-an-object-is-a-promise/38339199#38339199 }); }); diff --git a/test/object-node.js b/test/object-node.js new file mode 100644 index 0000000..a9df47c --- /dev/null +++ b/test/object-node.js @@ -0,0 +1,48 @@ +/** + * Created by iddo on 2/13/18. + */ + +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const { expect, assert } = chai; +const { describe, it } = require('mocha'); +const ObjectNode = require('../src/Nodes/ObjectNode'); +const LeafNode = require('../src/Nodes/LeafNode'); + +describe('ObjectNode', () => { + + it('should handle valid objects properly', async () => { + let n = new ObjectNode('obj', [ + new LeafNode('a'), + new LeafNode('b') + ]); + + let v = await n.eval({ + obj: { + a: 'AA', + b: 'BB' + } + }); + + assert.equal(v.a, 'AA'); + assert.equal(v.b, 'BB'); + }); + + it('should support referencing properties from outside object within object', async () => { + let n = new ObjectNode('obj', [ + new LeafNode('a'), + new LeafNode('b') + ]); + + let v = await n.eval({ + obj: { + a: 'AA' + }, + b: 'BB' + }); + + assert.equal(v.a, 'AA'); + assert.equal(v.b, 'BB'); + }); +}); + diff --git a/test/parser.js b/test/parser.js index 7183635..3bbae11 100644 --- a/test/parser.js +++ b/test/parser.js @@ -9,12 +9,50 @@ const assert = require('assert'), RenameNode = require('../src/Nodes/RenameNode'), OptionalNode = require('../src/Nodes/OptionalNode'), CastedLeafNode = require('../src/Nodes/CastedLeafNode'), - FunctionNode = require('../src/Nodes/FunctionNode'); + FunctionNode = require('../src/Nodes/FunctionNode'), + LogicNode = require('../src/Nodes/LogicNode'), + FlatObjectNode = require('../src/Nodes/FlatObjectNode'), + CachedFunctionNode = require('../src/Nodes/CachedFunctionNode'); const parse = require('../src/Parser/Parser').parse; +const removeWhiteSpaces = require('../src/Parser/Parser').removeWhiteSpaces; +const removeComments = require('../src/Parser/Parser').removeComments; describe('Parser', () => { + + describe('removeWhiteSpaces', () => { + it('should remove white spaces from simple string', () => { + assert.equal('sdfsdfsdf', removeWhiteSpaces('sdf sdf sdf')); + }); + + it('should ignore white spaces in double quoted string', () => { + assert.equal('sdfsdfsdf"iddo is awesome"', removeWhiteSpaces('sdf sdf sdf "iddo is awesome"')); + }); + + it('should ignore escaped double quoted string', () => { + assert.equal('sdfsdfsdf"iddo is \"awesome\""', removeWhiteSpaces('sdf sdf sdf "iddo is \"awesome\""')); + }); + }); + + describe('removeComments', () => { + it('should return empty string if line if it starts with //', () => { + assert.equal('', removeComments('// sdf sdf sdf')); + }); + + it('should return string normally if it does not start with //', () => { + assert.equal('sdf sdf sdf', removeComments('sdf sdf sdf')); + }) + + it('should return lines that do not include // but remove lines that do have //', () => { + assert.equal('sdf sdf sdf \n', removeComments('sdf sdf sdf \n // sdf sdf sdf')); + }) + + it('should return lines that do not include // but remove lines that do have //', () => { + assert.equal('\n sdf sdf sdf', removeComments('// sdf sdf sdf \n sdf sdf sdf')); + }) + }); + describe('Basics', () => { it('should return empty query', (done) => { parse(`{}`).then(val => { @@ -69,6 +107,27 @@ describe('Parser', () => { }); }); + it('should detect quoted 2 leaf nodes and remove quotes', (done) => { + parse(`{ + "b", + 'c' + }`).then((val) => { + assert.equal(val.length, 2); // Exactly 2 nodes + + //Node 1 + assert.equal(val[0] instanceof LeafNode, true); // Leaf node + assert.equal(val[0].getName(), 'b'); //Name is b + + //Node 2 + assert.equal(val[1] instanceof LeafNode, true); // Leaf node + assert.equal(val[1].getName(), 'c'); //Name is b + + done(); + }).catch((err) => { + done(`Rejected with error ${err}`); + }); + }); + it('should support composite nodes', (done) => { parse(`{ a { @@ -116,6 +175,158 @@ describe('Parser', () => { }); }); + it('should support logic nodes', async () => { + const val = await parse(`{ + @if(key1:val1, key2:val2) { + a + } + }`); + assert.equal(val.length, 1); + assert.equal(val[0] instanceof LogicNode, true); + assert.equal(val[0].args['key1'], "val1"); //Check simple arg + assert.equal(val[0].args['key2'], "val2"); //Check simple arg + + }); + + it('should support logic node with single follow on (else)', async () => { + const val = await parse(`{ + @if (key1:val1, key2:val2) { + a + } @else (key1:val3, key2:val4) { + a + } + }`); + + // Check @if node + assert.equal(val.length, 1); + assert.equal(val[0] instanceof LogicNode, true); + assert.equal(val[0].args['key1'], "val1"); //Check simple arg + assert.equal(val[0].args['key2'], "val2"); //Check simple arg + + // Check @else node + assert.equal(!!val[0].followOnNode, true); + assert.equal(val[0].followOnNode instanceof LogicNode, true); + assert.equal(val[0].followOnNode.args['key1'], "val3"); + assert.equal(val[0].followOnNode.args['key2'], "val4"); + }); + + it('should support logic node with multiple follow ons (elseif, else)', async () => { + const val = await parse(`{ + @if (key1:val1, key2:val2) { + a + } @elseif (key1:val3, key2:val4) { + a + } @else (key1:val5, key2:val6) { + a + } + }`); + + // Check @if node + assert.equal(val.length, 1); + assert.equal(val[0] instanceof LogicNode, true); + assert.equal(val[0].args['key1'], "val1"); //Check simple arg + assert.equal(val[0].args['key2'], "val2"); //Check simple arg + + // Check @elseif node + assert.equal(!!val[0].followOnNode, true); + assert.equal(val[0].followOnNode instanceof LogicNode, true); + assert.equal(val[0].followOnNode.args['key1'], "val3"); + assert.equal(val[0].followOnNode.args['key2'], "val4"); + + // Check @else node + assert.equal(!!val[0].followOnNode.followOnNode, true); + assert.equal(val[0].followOnNode.followOnNode instanceof LogicNode, true); + assert.equal(val[0].followOnNode.followOnNode.args['key1'], "val5"); + assert.equal(val[0].followOnNode.followOnNode.args['key2'], "val6"); + }); + + describe('function nodes data types', () => { + it('should support variable names', async () => { + const val = await parse(`{ + RapidAPI.Name.Function(key1:val1, key2:val2) { + a + } + }`); + assert.equal(val.length, 1); // Exactly 1 root node + assert.equal(val[0].hasOwnProperty('args'), true); // Check type. Only function nodes have args (it can be sub-type) + assert.equal(val[0].args['key1'], "val1"); //Check simple arg + assert.equal(val[0].args['key2'], "val2"); //Check simple arg + }); + + it('should support string literals', async () => { + const val = await parse(`{ + RapidAPI.Name.Function(key1:"str1", key2:"str2") { + a + } + }`); + assert.equal(val.length, 1); // Exactly 1 root node + assert.equal(val[0].hasOwnProperty('args'), true); // Check type. Only function nodes have args (it can be sub-type) + assert.equal(val[0].args['key1'], '"str1"'); //Check simple arg + assert.equal(val[0].args['key2'], '"str2"'); //Check simple arg + }); + + it('should support string literals with spaces', async () => { + const val = await parse(`{ + RapidAPI.Name.Function(key1:"str1 part2", key2:"str2") { + a + } + }`); + assert.equal(val.length, 1); // Exactly 1 root node + assert.equal(val[0].hasOwnProperty('args'), true); // Check type. Only function nodes have args (it can be sub-type) + assert.equal(val[0].args['key1'], '"str1 part2"'); //Check simple arg + assert.equal(val[0].args['key2'], '"str2"'); //Check simple arg + }); + + it('should support number literals', async () => { + const val = await parse(`{ + RapidAPI.Name.Function(key1:123, key2:124.6, key3:-1, key4:0) { + a + } + }`); + assert.equal(val.length, 1); // Exactly 1 root node + assert.equal(val[0].hasOwnProperty('args'), true); // Check type. Only function nodes have args (it can be sub-type) + assert.equal(val[0].args['key1'], 123); //Check simple arg + assert.equal(typeof val[0].args['key1'], 'number'); //Check type + assert.equal(val[0].args['key2'], 124.6); //Check simple arg + assert.equal(typeof val[0].args['key2'], 'number'); //Check type + assert.equal(val[0].args['key3'], -1); //Check simple arg + assert.equal(typeof val[0].args['key3'], 'number'); //Check type + assert.equal(val[0].args['key4'], 0); //Check simple arg + assert.equal(typeof val[0].args['key4'], 'number'); //Check type + }); + + it('should support objects', async () => { + const val = await parse(`{ + RapidAPI.Name.Function(obj:{key1:"str1", key2:2}) { + a + } + }`); + assert.equal(val.length, 1); // Exactly 1 root node + assert.equal(val[0].hasOwnProperty('args'), true); // Check type. Only function nodes have args (it can be sub-type) + assert.equal(typeof val[0].args['obj'], 'object'); //Check simple arg + assert.equal(val[0].args['obj']['key1'], '"str1"'); //Check simple arg + assert.equal(val[0].args['obj']['key2'], 2); //Check simple arg + }); + + it('should support array', async () => { + const val = await parse(`{ + RapidAPI.Name.Function(arr:[key, "string", {k:"v"}]) { + a + } + }`); + assert.equal(val.length, 1); // Exactly 1 root node + assert.equal(val[0].hasOwnProperty('args'), true); // Check type. Only function nodes have args (it can be sub-type) + assert.equal(Array.isArray(val[0].args['arr']), true); + assert.equal(val[0].args['arr'].length, 3); + assert.equal(val[0].args['arr'][0], "key"); + assert.equal(val[0].args['arr'][1], '"string"'); + assert.equal(typeof val[0].args['arr'][2], 'object'); + assert.equal(val[0].args['arr'][2]['k'], '"v"'); + + }); + + }); + it('should fail with function nodes with unsupported names', (done) => { try { parse(`{ @@ -329,4 +540,75 @@ describe('Parser', () => { assert.equal(n.children[0].innerNode instanceof LeafNode, true); }); }); + + describe('cached function nodes', () => { + it('should support cached function nodes', async () => { + const val = await parse(` + { + *RapidAPI.Name.Function(key1:val1, key2:{subKey: subValue}) { + b + } + } + `); + let n = val[0]; + assert.equal(val.length, 1); // Only 1 node + assert.equal(n instanceof CachedFunctionNode, true); // Cached function node + assert.equal(n.getName(), "RapidAPI.Name.Function"); //Name should stay the same + assert.equal(n.innerNode instanceof FunctionNode, true); + assert.equal(n.innerNode.children.length, 1); + assert.equal(n.innerNode.children[0].getName(), "b"); + assert.equal(Object.keys(n.innerNode.args).length, 2); + }); + }); + + describe('FlatObjectNode', () => { + it('should support flat composite nodes nodes', async () => { + const val = await parse(` + { + -a { + b + } + } + `); + let n = val[0]; + assert.equal(val.length, 1); // Only 1 node + assert.equal(n instanceof FlatObjectNode, true); // Optional node + assert.equal(n.getName(), "a"); + assert.equal(n.innerNode instanceof CompositeNode, true); + assert.equal(n.innerNode.children.length, 1); + assert.equal(n.innerNode.children[0].getName(), "b"); + }); + + it('should support flat function nodes', async () => { + const val = await parse(` + { + -RapidAPI.Name.Function(key1:val1, key2:{subKey: subValue}) { + b + } + } + `); + let n = val[0]; + assert.equal(val.length, 1); // Only 1 node + assert.equal(n instanceof FlatObjectNode, true); // Optional node + assert.equal(n.getName(), "RapidAPI.Name.Function"); + assert.equal(n.innerNode instanceof FunctionNode, true); + assert.equal(n.innerNode.children.length, 1); + assert.equal(n.innerNode.children[0].getName(), "b"); + }); + + it('should support optional flat nodes', async () => { + const val = await parse(` + { + ?-b { + + } + } + `); + let n = val[0]; + assert.equal(val.length, 1); // Only 1 node + assert.equal(n instanceof OptionalNode, true); // Optional node + assert.equal(n.innerNode instanceof FlatObjectNode, true); // Optional node + assert.equal(n.innerNode.innerNode instanceof CompositeNode, true); // Optional node + }); + }); }); \ No newline at end of file diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..d7c9305 --- /dev/null +++ b/test/utils.js @@ -0,0 +1,109 @@ +/** + * Created by iddo on 8/31/17. + */ +"use strict"; + +const assert = require('assert'), + utils = require('../src/Nodes/utils'); + +describe('Utils', () => { + describe('resolve', () => { + it ('should resolve top level object', () => { + const testObj = {a:12}; + assert.equal(utils.resolve('a', testObj), testObj.a); + }); + + it ('should resolve low level object', () => { + const testObj = {a:{b: 12}}; + assert.equal(utils.resolve('a.b', testObj), testObj.a.b); + }); + + it ('should throw error on non-existent top level object', (done) => { + try { + const testObj = {a:12}; + let v = utils.resolve('c', testObj); + done(`Didn't fail. Returned value ${v}`); + } catch (e) { + assert.equal(e, `Name c does not exist in context { + "a": 12 +}`); + done(); + } + }); + + it ('should throw error accessing property of non-existent top level object', (done) => { + try { + const testObj = {a:12}; + let v = utils.resolve('c.d', testObj); + done(`Didn't fail. Returned value ${v}`); + } catch (e) { + assert.equal(e, `Name c.d does not exist in context { + "a": 12 +}`); + done(); + } + }); + }); + + describe('createMixedContext', () => { + it('should return inner context value if only in inner context', () => { + let context = utils.createMixedContext({}, {a:1}); + assert.equal(context.a, 1); + }); + + it('should return inner context value if only in outer context', () => { + let context = utils.createMixedContext({a:1}, {}); + assert.equal(context.a, 1); + }); + + it('should give precedence to inner context over outer context, even if property exists in both', () => { + let context = utils.createMixedContext({a: 2}, {a:1}); + assert.equal(context.a, 1); + }); + + it('should default to outer context if property not in inner context', () => { + let context = utils.createMixedContext({a:1}, {b:3}); + assert.equal(context.a, 1); + }); + + it ('should return undefined if property doesnt exist in both contexts', () => { + let context = utils.createMixedContext({a:1}, {b:3}); + assert.equal(context.c, undefined); + }); + + describe('in operator', () => { + it('should return true if in inner context', () => { + let context = utils.createMixedContext({}, {a:1}); + assert.equal(true, 'a' in context); + }); + + it('should return true if in outer context', () => { + let context = utils.createMixedContext({a:1}, {}); + assert.equal(true, 'a' in context); + }); + + it('should return false if not in any context', () => { + let context = utils.createMixedContext({a:1}, {b:2}); + assert.equal(false, 'c' in context); + }); + }); + + describe('hasOwnProperty operator', () => { + it('should return true if in inner context', () => { + let context = utils.createMixedContext({}, {a:1}); + assert.equal(true, context.hasOwnProperty('a')); + }); + + it('should return true if in outer context', () => { + let context = utils.createMixedContext({a:1}, {}); + assert.equal(true, context.hasOwnProperty('a')); + }); + + it('should return false if not in any context', () => { + let context = utils.createMixedContext({a:1}, {b:2}); + assert.equal(false, context.hasOwnProperty('c')); + }); + }); + + }); +}); \ No newline at end of file