diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..26fb431 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +src/ +dist/ \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index 738756d..0471021 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,9 @@ { + "parser": "@typescript-eslint/parser", "extends": [ - "eslint:recommended" + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" ], "env": { "node": true, @@ -9,11 +12,25 @@ }, "parserOptions": { "ecmaVersion": 2018, - "sourceType": "module", - "allowImportExportEverywhere": true + "sourceType": "module" }, "rules": { "no-var": "error", - "no-console": "off" + "no-console": "off", + "@typescript-eslint/camelcase": "off", + "@typescript-eslint/interface-name-prefix": "off", + "@typescript-eslint/member-delimiter-style": [ + "error", + { + "multiline": { + "delimiter": "semi", + "requireLast": true + }, + "singleline": { + "delimiter": "semi", + "requireLast": true + } + } + ] } } \ No newline at end of file diff --git a/.github/workflows/dockerimage.yml b/.github/workflows/dockerimage.yml new file mode 100644 index 0000000..63eb23c --- /dev/null +++ b/.github/workflows/dockerimage.yml @@ -0,0 +1,20 @@ +name: Docker Image CI + +on: + push: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag peerjs/peerjs-server:latest + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@master + with: + name: peerjs/peerjs-server:latest + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.gitignore b/.gitignore index e372c24..16bd87a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,6 @@ results node_modules npm-debug.log -.idea \ No newline at end of file +.idea +.cache +.vscode \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..2e958b0 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,7 @@ +tasks: + - init: npm i + command: npm start + +ports: + - port: 9000 + onOpen: open-preview diff --git a/Dockerfile b/Dockerfile index 4455519..1e896fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,8 @@ FROM node:alpine RUN mkdir /peer-server WORKDIR /peer-server COPY bin ./bin +COPY dist ./dist COPY package.json . -COPY src ./src -COPY config ./config -COPY app.json . RUN npm install EXPOSE 9000 ENTRYPOINT ["node", "bin/peerjs"] diff --git a/README.md b/README.md index 3564c94..16fd9a3 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,34 @@ [![Build Status](https://travis-ci.org/peers/peerjs-server.png?branch=master)](https://travis-ci.org/peers/peerjs-server) +[![npm version](https://badge.fury.io/js/peer.svg)](https://www.npmjs.com/package/peer) +[![Downloads](https://img.shields.io/npm/dm/peer.svg)](https://www.npmjs.com/package/peer) # PeerServer: A server for PeerJS # -This fork of peerjs-server adds functionality to set a custom ID generation fucntion. [Commit](https://github.com/ajmar/peerjs-server/commit/2552e9d) - PeerServer helps broker connections between PeerJS clients. Data is not proxied through the server. +Run your own server on Gitpod! + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/peers/peerjs-server) + ## [https://peerjs.com](https://peerjs.com) ### Run PeerServer -1. Clone app: +1. Install PeerServer from npm or github: + +#### NPM ```bash -git clone https://github.com/peers/peerjs-server.git +npm install peer ``` -2. Install dependencies: +#### github + ```bash +git clone https://github.com/peers/peerjs-server.git#master npm install ``` -3. Run the server: +2. Run the server: ```bash $> peerjs --port 9000 --key peerjs --path /myapp @@ -38,7 +46,9 @@ import {PeerServer} from 'peerjs-server'; const server = PeerServer({port: 9000, path: '/myapp'}); ``` -Connecting to the server from PeerJS: +3. Check that server works: open browser with [http://localhost:9000/myapp](http://localhost:9000/myapp) It should returns JSON with name, description and website fields. + +### Connecting to the server from PeerJS: ```html ``` -Using HTTPS: Simply pass in PEM-encoded certificate and key. +### Using HTTPS: Simply pass in PEM-encoded certificate and key. ```javascript import fs from 'fs'; @@ -61,7 +71,7 @@ const server = PeerServer({ }); ``` -#### Running PeerServer behind a reverse proxy +### Running PeerServer behind a reverse proxy Make sure to set the `proxied` option, otherwise IP based limiting will fail. The option is passed verbatim to the @@ -74,6 +84,20 @@ import {PeerServer} from 'peerjs-server'; const server = PeerServer({port: 9000, path: '/myapp', proxied: true}); ``` + +### Custom client ID generation + +You can specify a custom function to use to generate client IDs. + +```javascript +const genRandomId = () => { + // Original generation algorithm + return (Math.random().toString(36) + '0000000000000000000').substr(2, 16); +} + +const server = PeerServer({port: 9000, path: '/myapp', proxied: true, genRandomId: genRandomId }); +``` + ### Combining with existing express app ```javascript @@ -146,7 +170,7 @@ This will start a peerjs server on port 9000 exposed on port 9000. ## Problems? -Discuss PeerJS on our Google Group: -https://groups.google.com/forum/?fromgroups#!forum/peerjs +Discuss PeerJS on our Telegram chat: +https://t.me/joinchat/ENhPuhTvhm8WlIxTjQf7Og Please post any bugs as a Github issue. diff --git a/bin/peerjs b/bin/peerjs index 01c4a8f..9d45ca8 100755 --- a/bin/peerjs +++ b/bin/peerjs @@ -1,69 +1,70 @@ #!/usr/bin/env node +// tslint:disable -const path = require('path'); -const pkg = require('../package.json'); -const fs = require('fs'); +const path = require("path"); +const pkg = require("../package.json"); +const fs = require("fs"); const version = pkg.version; -const PeerServer = require('../src').PeerServer; -const opts = require('optimist') - .usage('Usage: $0') +const { PeerServer } = require("../dist/src"); +const opts = require("optimist") + .usage("Usage: $0") .options({ expire_timeout: { demand: false, - alias: 't', - description: 'timeout (milliseconds)', + alias: "t", + description: "timeout (milliseconds)", default: 5000 }, concurrent_limit: { demand: false, - alias: 'c', - description: 'concurrent limit', + alias: "c", + description: "concurrent limit", default: 5000 }, alive_timeout: { demand: false, - description: 'broken connection check timeout (milliseconds)', + description: "broken connection check timeout (milliseconds)", default: 60000 }, key: { demand: false, - alias: 'k', - description: 'connection key', - default: 'peerjs' + alias: "k", + description: "connection key", + default: "peerjs" }, sslkey: { demand: false, - description: 'path to SSL key' + description: "path to SSL key" }, sslcert: { demand: false, - description: 'path to SSL certificate' + description: "path to SSL certificate" }, port: { demand: true, - alias: 'p', - description: 'port' + alias: "p", + description: "port" }, path: { demand: false, - description: 'custom path', - default: '/' + description: "custom path", + default: "/" }, allow_discovery: { demand: false, - description: 'allow discovery of peers' + description: "allow discovery of peers" }, proxied: { demand: false, - description: 'Set true if PeerServer stays behind a reverse proxy', + description: "Set true if PeerServer stays behind a reverse proxy", default: false } }) - .boolean('allow_discovery') + .boolean("allow_discovery") .argv; -process.on('uncaughtException', function (e) { - console.error('Error: ' + e); +process.on("uncaughtException", function (e) { + console.error("Error: " + e); }); if (opts.sslkey || opts.sslcert) { @@ -76,8 +77,8 @@ if (opts.sslkey || opts.sslcert) { delete opts.sslkey; delete opts.sslcert; } else { - console.error('Warning: PeerServer will not run because either ' + - 'the key or the certificate has not been provided.'); + console.error("Warning: PeerServer will not run because either " + + "the key or the certificate has not been provided."); process.exit(1); } } @@ -88,15 +89,15 @@ const server = PeerServer(opts, server => { const port = server.address().port; console.log( - 'Started PeerServer on %s, port: %s, path: %s (v. %s)', - host, port, userPath || '/', version + "Started PeerServer on %s, port: %s, path: %s (v. %s)", + host, port, userPath || "/", version ); }); -server.on('connection', client => { +server.on("connection", client => { console.log(`Client connected: ${client.getId()}`); }); -server.on('disconnect', client => { +server.on("disconnect", client => { console.log(`Client disconnected: ${client.getId()}`); }); diff --git a/changelog.md b/changelog.md index 8f8987f..5701358 100644 --- a/changelog.md +++ b/changelog.md @@ -2,8 +2,10 @@ ### vNEXT 0.3.0 -* refactoring (add ESLint, split code into small unit) Thanks to @d07RiV @zhou-yg -* update deps +* Convert project to TypeScript 3.7.3. +* Use UUID when generate client id - #152 +* Refactoring (add ESLint, split code into small unit) Thanks to @d07RiV @zhou-yg +* Update deps. ### 0.2.6 diff --git a/config/index.js b/config/index.js deleted file mode 100644 index 3b0ecb4..0000000 --- a/config/index.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - port: 9000, - expire_timeout: 5000, - alive_timeout: 60000, - key: 'peerjs', - path: '/myapp', - concurrent_limit: 5000, - allow_discovery: false, - proxied: false, - cleanup_out_msgs: 1000, - ssl: { - key: '', - cert: '' - } -}; diff --git a/dist/app.json b/dist/app.json new file mode 100644 index 0000000..6490e45 --- /dev/null +++ b/dist/app.json @@ -0,0 +1,5 @@ +{ + "name": "PeerJS Server", + "description": "A server side element to broker connections between PeerJS clients.", + "website": "http://peerjs.com/" +} diff --git a/dist/config/index.js b/dist/config/index.js new file mode 100644 index 0000000..b95d622 --- /dev/null +++ b/dist/config/index.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const defaultConfig = { + port: 9000, + expire_timeout: 5000, + alive_timeout: 60000, + key: "peerjs", + path: "/myapp", + concurrent_limit: 5000, + allow_discovery: false, + proxied: false, + cleanup_out_msgs: 1000, + ssl: { + key: "", + cert: "" + } +}; +exports.default = defaultConfig; diff --git a/dist/src/api/index.js b/dist/src/api/index.js new file mode 100644 index 0000000..ed7fabc --- /dev/null +++ b/dist/src/api/index.js @@ -0,0 +1,24 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const body_parser_1 = __importDefault(require("body-parser")); +const cors_1 = __importDefault(require("cors")); +const express_1 = __importDefault(require("express")); +const app_json_1 = __importDefault(require("../../app.json")); +const auth_1 = require("./middleware/auth"); +const calls_1 = __importDefault(require("./v1/calls")); +const public_1 = __importDefault(require("./v1/public")); +exports.Api = ({ config, realm, messageHandler }) => { + const authMiddleware = new auth_1.AuthMiddleware(config, realm); + const app = express_1.default.Router(); + const jsonParser = body_parser_1.default.json(); + app.use(cors_1.default()); + app.get("/", (_, res) => { + res.send(app_json_1.default); + }); + app.use("/:key", public_1.default({ config, realm })); + app.use("/:key/:id/:token", authMiddleware.handle, jsonParser, calls_1.default({ realm, messageHandler })); + return app; +}; diff --git a/dist/src/api/middleware/auth/index.js b/dist/src/api/middleware/auth/index.js new file mode 100644 index 0000000..11d15ca --- /dev/null +++ b/dist/src/api/middleware/auth/index.js @@ -0,0 +1,27 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const enums_1 = require("../../../enums"); +class AuthMiddleware { + constructor(config, realm) { + this.config = config; + this.realm = realm; + } + handle(req, res, next) { + const { id, token, key } = req.params; + if (key !== this.config.key) { + return res.status(401).send(enums_1.Errors.INVALID_KEY); + } + if (!id) { + return res.sendStatus(401); + } + const client = this.realm.getClientById(id); + if (!client) { + return res.sendStatus(401); + } + if (client.getToken() && token !== client.getToken()) { + return res.status(401).send(enums_1.Errors.INVALID_TOKEN); + } + next(); + } +} +exports.AuthMiddleware = AuthMiddleware; diff --git a/dist/src/api/middleware/middleware.js b/dist/src/api/middleware/middleware.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/dist/src/api/middleware/middleware.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/dist/src/api/v1/calls/index.js b/dist/src/api/v1/calls/index.js new file mode 100644 index 0000000..17ae5e3 --- /dev/null +++ b/dist/src/api/v1/calls/index.js @@ -0,0 +1,32 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = __importDefault(require("express")); +exports.default = ({ realm, messageHandler }) => { + const app = express_1.default.Router(); + const handle = (req, res, next) => { + const { id } = req.params; + if (!id) + return next(); + const client = realm.getClientById(id); + if (!client) { + throw new Error(`client not found:${id}`); + } + const { type, dst, payload } = req.body; + const message = { + type, + src: id, + dst, + payload + }; + messageHandler.handle(client, message); + res.sendStatus(200); + }; + app.post("/offer", handle); + app.post("/candidate", handle); + app.post("/answer", handle); + app.post("/leave", handle); + return app; +}; diff --git a/dist/src/api/v1/public/index.js b/dist/src/api/v1/public/index.js new file mode 100644 index 0000000..1bf69f9 --- /dev/null +++ b/dist/src/api/v1/public/index.js @@ -0,0 +1,23 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = __importDefault(require("express")); +exports.default = ({ config, realm }) => { + const app = express_1.default.Router(); + // Retrieve guaranteed random ID. + app.get("/id", (_, res) => { + res.contentType("html"); + res.send(realm.generateClientId()); + }); + // Get a list of all peers for a key, enabled by the `allowDiscovery` flag. + app.get("/peers", (_, res) => { + if (config.allow_discovery) { + const clientsIds = realm.getClientsIds(); + return res.send(clientsIds); + } + res.sendStatus(401); + }); + return app; +}; diff --git a/dist/src/config/index.js b/dist/src/config/index.js new file mode 100644 index 0000000..b95d622 --- /dev/null +++ b/dist/src/config/index.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const defaultConfig = { + port: 9000, + expire_timeout: 5000, + alive_timeout: 60000, + key: "peerjs", + path: "/myapp", + concurrent_limit: 5000, + allow_discovery: false, + proxied: false, + cleanup_out_msgs: 1000, + ssl: { + key: "", + cert: "" + } +}; +exports.default = defaultConfig; diff --git a/dist/src/enums.js b/dist/src/enums.js new file mode 100644 index 0000000..7670504 --- /dev/null +++ b/dist/src/enums.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Errors; +(function (Errors) { + Errors["INVALID_KEY"] = "Invalid key provided"; + Errors["INVALID_TOKEN"] = "Invalid token provided"; + Errors["INVALID_WS_PARAMETERS"] = "No id, token, or key supplied to websocket server"; + Errors["CONNECTION_LIMIT_EXCEED"] = "Server has reached its concurrent user limit"; +})(Errors = exports.Errors || (exports.Errors = {})); +var MessageType; +(function (MessageType) { + MessageType["OPEN"] = "OPEN"; + MessageType["LEAVE"] = "LEAVE"; + MessageType["CANDIDATE"] = "CANDIDATE"; + MessageType["OFFER"] = "OFFER"; + MessageType["ANSWER"] = "ANSWER"; + MessageType["EXPIRE"] = "EXPIRE"; + MessageType["HEARTBEAT"] = "HEARTBEAT"; + MessageType["ID_TAKEN"] = "ID-TAKEN"; + MessageType["ERROR"] = "ERROR"; +})(MessageType = exports.MessageType || (exports.MessageType = {})); diff --git a/dist/src/index.js b/dist/src/index.js new file mode 100644 index 0000000..dc4a6fb --- /dev/null +++ b/dist/src/index.js @@ -0,0 +1,52 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = __importDefault(require("express")); +const http_1 = __importDefault(require("http")); +const https_1 = __importDefault(require("https")); +const config_1 = __importDefault(require("./config")); +const instance_1 = require("./instance"); +function ExpressPeerServer(server, options) { + const app = express_1.default(); + const newOptions = Object.assign(Object.assign({}, config_1.default), options); + if (newOptions.proxied) { + app.set("trust proxy", newOptions.proxied === "false" ? false : !!newOptions.proxied); + } + app.on("mount", () => { + if (!server) { + throw new Error("Server is not passed to constructor - " + + "can't start PeerServer"); + } + instance_1.createInstance({ app, server, options: newOptions }); + }); + return app; +} +exports.ExpressPeerServer = ExpressPeerServer; +function PeerServer(options = {}, callback) { + const app = express_1.default(); + const newOptions = Object.assign(Object.assign({}, config_1.default), options); + let path = newOptions.path; + const port = newOptions.port; + if (!path.startsWith('/')) { + path = "/" + path; + } + if (!path.endsWith('/')) { + path += "/"; + } + let server; + if (newOptions.ssl && newOptions.ssl.key && newOptions.ssl.cert) { + server = https_1.default.createServer(options.ssl, app); + // @ts-ignore + delete newOptions.ssl; + } + else { + server = http_1.default.createServer(app); + } + const peerjs = ExpressPeerServer(server, newOptions); + app.use(peerjs); + server.listen(port, () => { var _a; return (_a = callback) === null || _a === void 0 ? void 0 : _a(server); }); + return peerjs; +} +exports.PeerServer = PeerServer; diff --git a/dist/src/instance.js b/dist/src/instance.js new file mode 100644 index 0000000..458e01c --- /dev/null +++ b/dist/src/instance.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const realm_1 = require("./models/realm"); +const checkBrokenConnections_1 = require("./services/checkBrokenConnections"); +const messagesExpire_1 = require("./services/messagesExpire"); +const webSocketServer_1 = require("./services/webSocketServer"); +const messageHandler_1 = require("./messageHandler"); +const api_1 = require("./api"); +exports.createInstance = ({ app, server, options }) => { + const config = options; + const realm = new realm_1.Realm(); + const messageHandler = new messageHandler_1.MessageHandler(realm); + const api = api_1.Api({ config, realm, messageHandler }); + const messagesExpire = new messagesExpire_1.MessagesExpire({ realm, config, messageHandler }); + const checkBrokenConnections = new checkBrokenConnections_1.CheckBrokenConnections({ + realm, + config, + onClose: client => { + app.emit("disconnect", client); + } + }); + app.use(options.path, api); + const wss = new webSocketServer_1.WebSocketServer({ + server, + realm, + config + }); + wss.on("connection", (client) => { + const messageQueue = realm.getMessageQueueById(client.getId()); + if (messageQueue) { + let message; + while (message = messageQueue.readMessage()) { + messageHandler.handle(client, message); + } + realm.clearMessageQueue(client.getId()); + } + app.emit("connection", client); + }); + wss.on("message", (client, message) => { + app.emit("message", client, message); + messageHandler.handle(client, message); + }); + wss.on("close", (client) => { + app.emit("disconnect", client); + }); + wss.on("error", (error) => { + app.emit("error", error); + }); + messagesExpire.startMessagesExpiration(); + checkBrokenConnections.start(); +}; diff --git a/dist/src/messageHandler/handler.js b/dist/src/messageHandler/handler.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/dist/src/messageHandler/handler.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/dist/src/messageHandler/handlers/heartbeat/index.js b/dist/src/messageHandler/handlers/heartbeat/index.js new file mode 100644 index 0000000..d292fec --- /dev/null +++ b/dist/src/messageHandler/handlers/heartbeat/index.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HeartbeatHandler = (client) => { + if (client) { + const nowTime = new Date().getTime(); + client.setLastPing(nowTime); + } + return true; +}; diff --git a/dist/src/messageHandler/handlers/index.js b/dist/src/messageHandler/handlers/index.js new file mode 100644 index 0000000..f053a44 --- /dev/null +++ b/dist/src/messageHandler/handlers/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var heartbeat_1 = require("./heartbeat"); +exports.HeartbeatHandler = heartbeat_1.HeartbeatHandler; +var transmission_1 = require("./transmission"); +exports.TransmissionHandler = transmission_1.TransmissionHandler; diff --git a/dist/src/messageHandler/handlers/transmission/index.js b/dist/src/messageHandler/handlers/transmission/index.js new file mode 100644 index 0000000..5183eb4 --- /dev/null +++ b/dist/src/messageHandler/handlers/transmission/index.js @@ -0,0 +1,58 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const enums_1 = require("../../../enums"); +exports.TransmissionHandler = ({ realm }) => { + const handle = (client, message) => { + const type = message.type; + const srcId = message.src; + const dstId = message.dst; + const destinationClient = realm.getClientById(dstId); + // User is connected! + if (destinationClient) { + const socket = destinationClient.getSocket(); + try { + if (socket) { + const data = JSON.stringify(message); + socket.send(data); + } + else { + // Neither socket no res available. Peer dead? + throw new Error("Peer dead"); + } + } + catch (e) { + // This happens when a peer disconnects without closing connections and + // the associated WebSocket has not closed. + // Tell other side to stop trying. + if (socket) { + socket.close(); + } + else { + realm.removeClientById(destinationClient.getId()); + } + handle(client, { + type: enums_1.MessageType.LEAVE, + src: dstId, + dst: srcId + }); + } + } + else { + // Wait for this client to connect/reconnect (XHR) for important + // messages. + const ignoredTypes = [enums_1.MessageType.LEAVE, enums_1.MessageType.EXPIRE]; + if (!ignoredTypes.includes(type) && dstId) { + realm.addMessageToQueue(dstId, message); + } + else if (type === enums_1.MessageType.LEAVE && !dstId) { + realm.removeClientById(srcId); + } + else { + // Unavailable destination specified with message LEAVE or EXPIRE + // Ignore + } + } + return true; + }; + return handle; +}; diff --git a/dist/src/messageHandler/handlersRegistry.js b/dist/src/messageHandler/handlersRegistry.js new file mode 100644 index 0000000..6875bd7 --- /dev/null +++ b/dist/src/messageHandler/handlersRegistry.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class HandlersRegistry { + constructor() { + this.handlers = new Map(); + } + registerHandler(messageType, handler) { + if (this.handlers.has(messageType)) + return; + this.handlers.set(messageType, handler); + } + handle(client, message) { + const { type } = message; + const handler = this.handlers.get(type); + if (!handler) + return false; + return handler(client, message); + } +} +exports.HandlersRegistry = HandlersRegistry; diff --git a/dist/src/messageHandler/index.js b/dist/src/messageHandler/index.js new file mode 100644 index 0000000..c8393ca --- /dev/null +++ b/dist/src/messageHandler/index.js @@ -0,0 +1,31 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const enums_1 = require("../enums"); +const handlers_1 = require("./handlers"); +const handlersRegistry_1 = require("./handlersRegistry"); +class MessageHandler { + constructor(realm, handlersRegistry = new handlersRegistry_1.HandlersRegistry()) { + this.handlersRegistry = handlersRegistry; + const transmissionHandler = handlers_1.TransmissionHandler({ realm }); + const heartbeatHandler = handlers_1.HeartbeatHandler; + const handleTransmission = (client, { type, src, dst, payload }) => { + return transmissionHandler(client, { + type, + src, + dst, + payload, + }); + }; + const handleHeartbeat = (client, message) => heartbeatHandler(client, message); + this.handlersRegistry.registerHandler(enums_1.MessageType.HEARTBEAT, handleHeartbeat); + this.handlersRegistry.registerHandler(enums_1.MessageType.OFFER, handleTransmission); + this.handlersRegistry.registerHandler(enums_1.MessageType.ANSWER, handleTransmission); + this.handlersRegistry.registerHandler(enums_1.MessageType.CANDIDATE, handleTransmission); + this.handlersRegistry.registerHandler(enums_1.MessageType.LEAVE, handleTransmission); + this.handlersRegistry.registerHandler(enums_1.MessageType.EXPIRE, handleTransmission); + } + handle(client, message) { + return this.handlersRegistry.handle(client, message); + } +} +exports.MessageHandler = MessageHandler; diff --git a/dist/src/messageHandler/messageHandlers.js b/dist/src/messageHandler/messageHandlers.js new file mode 100644 index 0000000..01b1e06 --- /dev/null +++ b/dist/src/messageHandler/messageHandlers.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class MessageHandlers { + constructor() { + this.handlers = new Map(); + } + registerHandler(messageType, handler) { + if (this.handlers.has(messageType)) + return; + this.handlers.set(messageType, handler); + } + handle(client, message) { + const { type } = message; + const handler = this.handlers.get(type); + if (!handler) + return false; + return handler(client, message); + } +} +exports.MessageHandlers = MessageHandlers; diff --git a/dist/src/models/client.js b/dist/src/models/client.js new file mode 100644 index 0000000..79473e4 --- /dev/null +++ b/dist/src/models/client.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class Client { + constructor({ id, token }) { + this.socket = null; + this.lastPing = new Date().getTime(); + this.id = id; + this.token = token; + } + getId() { + return this.id; + } + getToken() { + return this.token; + } + getSocket() { + return this.socket; + } + setSocket(socket) { + this.socket = socket; + } + getLastPing() { + return this.lastPing; + } + setLastPing(lastPing) { + this.lastPing = lastPing; + } + send(data) { + var _a; + (_a = this.socket) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(data)); + } +} +exports.Client = Client; diff --git a/dist/src/models/message.js b/dist/src/models/message.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/dist/src/models/message.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/dist/src/models/messageQueue.js b/dist/src/models/messageQueue.js new file mode 100644 index 0000000..fc04ea6 --- /dev/null +++ b/dist/src/models/messageQueue.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class MessageQueue { + constructor() { + this.lastReadAt = new Date().getTime(); + this.messages = []; + } + getLastReadAt() { + return this.lastReadAt; + } + addMessage(message) { + this.messages.push(message); + } + readMessage() { + if (this.messages.length > 0) { + this.lastReadAt = new Date().getTime(); + return this.messages.shift(); + } + return undefined; + } + getMessages() { + return this.messages; + } +} +exports.MessageQueue = MessageQueue; diff --git a/dist/src/models/realm.js b/dist/src/models/realm.js new file mode 100644 index 0000000..53fc672 --- /dev/null +++ b/dist/src/models/realm.js @@ -0,0 +1,52 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const v4_1 = __importDefault(require("uuid/v4")); +const messageQueue_1 = require("./messageQueue"); +class Realm { + constructor() { + this.clients = new Map(); + this.messageQueues = new Map(); + } + getClientsIds() { + return [...this.clients.keys()]; + } + getClientById(clientId) { + return this.clients.get(clientId); + } + getClientsIdsWithQueue() { + return [...this.messageQueues.keys()]; + } + setClient(client, id) { + this.clients.set(id, client); + } + removeClientById(id) { + const client = this.getClientById(id); + if (!client) + return false; + this.clients.delete(id); + return true; + } + getMessageQueueById(id) { + return this.messageQueues.get(id); + } + addMessageToQueue(id, message) { + if (!this.getMessageQueueById(id)) { + this.messageQueues.set(id, new messageQueue_1.MessageQueue()); + } + this.getMessageQueueById(id).addMessage(message); + } + clearMessageQueue(id) { + this.messageQueues.delete(id); + } + generateClientId() { + let clientId = v4_1.default(); + while (this.getClientById(clientId)) { + clientId = v4_1.default(); + } + return clientId; + } +} +exports.Realm = Realm; diff --git a/dist/src/services/checkBrokenConnections/index.js b/dist/src/services/checkBrokenConnections/index.js new file mode 100644 index 0000000..f0e7115 --- /dev/null +++ b/dist/src/services/checkBrokenConnections/index.js @@ -0,0 +1,50 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const DEFAULT_CHECK_INTERVAL = 300; +class CheckBrokenConnections { + constructor({ realm, config, checkInterval = DEFAULT_CHECK_INTERVAL, onClose }) { + this.timeoutId = null; + this.realm = realm; + this.config = config; + this.onClose = onClose; + this.checkInterval = checkInterval; + } + start() { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + this.timeoutId = setTimeout(() => { + this.checkConnections(); + this.timeoutId = null; + this.start(); + }, this.checkInterval); + } + stop() { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + this.timeoutId = null; + } + } + checkConnections() { + var _a, _b, _c; + const clientsIds = this.realm.getClientsIds(); + const now = new Date().getTime(); + const { alive_timeout: aliveTimeout } = this.config; + for (const clientId of clientsIds) { + const client = this.realm.getClientById(clientId); + const timeSinceLastPing = now - client.getLastPing(); + if (timeSinceLastPing < aliveTimeout) + continue; + try { + (_a = client.getSocket()) === null || _a === void 0 ? void 0 : _a.close(); + } + finally { + this.realm.clearMessageQueue(clientId); + this.realm.removeClientById(clientId); + client.setSocket(null); + (_c = (_b = this).onClose) === null || _c === void 0 ? void 0 : _c.call(_b, client); + } + } + } +} +exports.CheckBrokenConnections = CheckBrokenConnections; diff --git a/dist/src/services/messagesExpire/index.js b/dist/src/services/messagesExpire/index.js new file mode 100644 index 0000000..9e9d6f2 --- /dev/null +++ b/dist/src/services/messagesExpire/index.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const enums_1 = require("../../enums"); +class MessagesExpire { + constructor({ realm, config, messageHandler }) { + this.timeoutId = null; + this.realm = realm; + this.config = config; + this.messageHandler = messageHandler; + } + startMessagesExpiration() { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + // Clean up outstanding messages + this.timeoutId = setTimeout(() => { + this.pruneOutstanding(); + this.timeoutId = null; + this.startMessagesExpiration(); + }, this.config.cleanup_out_msgs); + } + stopMessagesExpiration() { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + this.timeoutId = null; + } + } + pruneOutstanding() { + const destinationClientsIds = this.realm.getClientsIdsWithQueue(); + const now = new Date().getTime(); + const maxDiff = this.config.expire_timeout; + const seen = {}; + for (const destinationClientId of destinationClientsIds) { + const messageQueue = this.realm.getMessageQueueById(destinationClientId); + const lastReadDiff = now - messageQueue.getLastReadAt(); + if (lastReadDiff < maxDiff) + continue; + const messages = messageQueue.getMessages(); + for (const message of messages) { + if (!seen[message.src]) { + this.messageHandler.handle(undefined, { + type: enums_1.MessageType.EXPIRE, + src: message.dst, + dst: message.src + }); + seen[message.src] = true; + } + } + this.realm.clearMessageQueue(destinationClientId); + } + } +} +exports.MessagesExpire = MessagesExpire; diff --git a/dist/src/services/webSocketServer/index.js b/dist/src/services/webSocketServer/index.js new file mode 100644 index 0000000..fde0b03 --- /dev/null +++ b/dist/src/services/webSocketServer/index.js @@ -0,0 +1,92 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const events_1 = __importDefault(require("events")); +const url_1 = __importDefault(require("url")); +const ws_1 = __importDefault(require("ws")); +const enums_1 = require("../../enums"); +const client_1 = require("../../models/client"); +const WS_PATH = 'peerjs'; +class WebSocketServer extends events_1.default { + constructor({ server, realm, config }) { + super(); + this.setMaxListeners(0); + this.realm = realm; + this.config = config; + const path = this.config.path; + this.path = `${path}${path.endsWith('/') ? "" : "/"}${WS_PATH}`; + this.socketServer = new ws_1.default.Server({ path: this.path, server }); + this.socketServer.on("connection", (socket, req) => this._onSocketConnection(socket, req)); + this.socketServer.on("error", (error) => this._onSocketError(error)); + } + _onSocketConnection(socket, req) { + const { query = {} } = url_1.default.parse(req.url, true); + const { id, token, key } = query; + if (!id || !token || !key) { + return this._sendErrorAndClose(socket, enums_1.Errors.INVALID_WS_PARAMETERS); + } + if (key !== this.config.key) { + return this._sendErrorAndClose(socket, enums_1.Errors.INVALID_KEY); + } + const client = this.realm.getClientById(id); + if (client) { + if (token !== client.getToken()) { + // ID-taken, invalid token + socket.send(JSON.stringify({ + type: enums_1.MessageType.ID_TAKEN, + payload: { msg: "ID is taken" } + })); + return socket.close(); + } + return this._configureWS(socket, client); + } + this._registerClient({ socket, id, token }); + } + _onSocketError(error) { + // handle error + this.emit("error", error); + } + _registerClient({ socket, id, token }) { + // Check concurrent limit + const clientsCount = this.realm.getClientsIds().length; + if (clientsCount >= this.config.concurrent_limit) { + return this._sendErrorAndClose(socket, enums_1.Errors.CONNECTION_LIMIT_EXCEED); + } + const newClient = new client_1.Client({ id, token }); + this.realm.setClient(newClient, id); + socket.send(JSON.stringify({ type: enums_1.MessageType.OPEN })); + this._configureWS(socket, newClient); + } + _configureWS(socket, client) { + client.setSocket(socket); + // Cleanup after a socket closes. + socket.on("close", () => { + if (client.getSocket() === socket) { + this.realm.removeClientById(client.getId()); + this.emit("close", client); + } + }); + // Handle messages from peers. + socket.on("message", (data) => { + try { + const message = JSON.parse(data); + message.src = client.getId(); + this.emit("message", client, message); + } + catch (e) { + this.emit("error", e); + } + }); + this.emit("connection", client); + } + _sendErrorAndClose(socket, msg) { + socket.send(JSON.stringify({ + type: enums_1.MessageType.ERROR, + payload: { msg } + })); + socket.close(); + } +} +exports.WebSocketServer = WebSocketServer; diff --git a/dist/src/services/webSocketServer/webSocket.js b/dist/src/services/webSocketServer/webSocket.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/dist/src/services/webSocketServer/webSocket.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/package-lock.json b/package-lock.json index 0131cba..3f43aaa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,14 +22,6 @@ "chalk": "^2.0.0", "esutils": "^2.0.2", "js-tokens": "^4.0.0" - }, - "dependencies": { - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - } } }, "@sinonjs/commons": { @@ -42,9 +34,9 @@ } }, "@sinonjs/formatio": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", - "integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", + "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", "dev": true, "requires": { "@sinonjs/commons": "^1", @@ -68,6 +60,209 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@types/body-parser": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", + "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-zw8UvoBEImn392tLjxoavuonblX/4Yb9ha4KBU10FirCfwgzhKO0dvyJSF9ByxV1xK1r2AgnAi/tvQaLgxQqxA==", + "dev": true + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "requires": { + "@types/node": "*" + } + }, + "@types/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-invOmosX0DqbpA+cE2yoHGUlF/blyf7nB0OGYBBiH27crcVm5NmFaZkLP4Ta1hGaesckCi5lVLlydNJCxkTOSg==", + "requires": { + "@types/express": "*" + } + }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, + "@types/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.1.tgz", + "integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.16.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz", + "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", + "requires": { + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "@types/json-schema": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", + "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", + "dev": true + }, + "@types/mime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", + "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==" + }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, + "@types/node": { + "version": "10.14.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.16.tgz", + "integrity": "sha512-/opXIbfn0P+VLt+N8DE4l8Mn8rbhiJgabU96ZJ0p9mxOkIks5gh6RUnpHak7Yh0SFkyjO/ODbxsQQPV2bpMmyA==" + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + }, + "@types/serve-static": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", + "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "@types/uuid": { + "version": "3.4.6", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.6.tgz", + "integrity": "sha512-cCdlC/1kGEZdEglzOieLDYBxHsvEOIg7kp/2FYyVR9Pxakq+Qf/inL3RKQ+PA8gOlI/NnL+fXmQH12nwcGzsHw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz", + "integrity": "sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==", + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.11.0.tgz", + "integrity": "sha512-G2HHA1vpMN0EEbUuWubiCCfd0R3a30BB+UdvnFkxwZIxYEGOrWEXDv8tBFO9f44CWc47Xv9lLM3VSn4ORLI2bA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.11.0", + "eslint-utils": "^1.4.3", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.11.0.tgz", + "integrity": "sha512-YxcA/y0ZJaCc/fB/MClhcDxHI0nOBB7v2/WxBju2cOTanX7jO9ttQq6Fy4yW9UaY5bPd9xL3cun3lDVqk67sPQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.11.0", + "eslint-scope": "^5.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.11.0.tgz", + "integrity": "sha512-DyGXeqhb3moMioEFZIHIp7oXBBh7dEfPTzGrlyP0Mi9ScCra4SWEGs3kPd18mG7Sy9Wy8z88zmrw5tSGL6r/6A==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.11.0", + "@typescript-eslint/typescript-estree": "2.11.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.11.0.tgz", + "integrity": "sha512-HGY4+d4MagO6cKMcKfIKaTMxcAv7dEVnji2Zi+vi5VV8uWAM631KjAB5GxFcexMYrwKT0EekRiiGK1/Sd7VFGA==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash.unescape": "4.0.1", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -78,15 +273,15 @@ } }, "acorn": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz", - "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", "dev": true }, "acorn-jsx": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", - "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", + "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", "dev": true }, "ajv": { @@ -101,6 +296,15 @@ "uri-js": "^4.2.2" } }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "dev": true, + "requires": { + "string-width": "^2.0.0" + } + }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", @@ -108,12 +312,12 @@ "dev": true }, "ansi-escapes": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz", - "integrity": "sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", + "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", "dev": true, "requires": { - "type-fest": "^0.5.2" + "type-fest": "^0.8.1" } }, "ansi-regex": { @@ -131,6 +335,33 @@ "color-convert": "^1.9.0" } }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "arg": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.2.tgz", + "integrity": "sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -140,6 +371,24 @@ "sprintf-js": "~1.0.2" } }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -151,15 +400,11 @@ "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", "dev": true }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" - } + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true }, "assertion-error": { "version": "1.1.0", @@ -167,23 +412,102 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -211,6 +535,29 @@ } } }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -221,17 +568,69 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -244,6 +643,12 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "capture-stack-trace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", + "dev": true + }, "chai": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", @@ -292,6 +697,84 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + } + } + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "dev": true + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -308,21 +791,59 @@ "dev": true }, "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "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==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } }, "color-convert": { "version": "1.9.3", @@ -339,17 +860,31 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "dev": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } }, "content-disposition": { "version": "0.5.3", @@ -374,15 +909,36 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", + "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", "requires": { "object-assign": "^4", "vary": "^1" } }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "dev": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -396,6 +952,12 @@ "which": "^1.2.9" } }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -404,18 +966,18 @@ "ms": "2.0.0" } }, - "debug-log": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", - "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", - "dev": true - }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -425,6 +987,12 @@ "type-detect": "^4.0.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -440,25 +1008,44 @@ "object-keys": "^1.0.12" } }, - "deglob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/deglob/-/deglob-4.0.0.tgz", - "integrity": "sha512-nrUWPTcQ4ap7KWCSeWNDXUgIe4A3nSbsp9cmWrxbKOOxUxLpAVzU1aeatYZfjE+lVHO1lUjXpBmoxOgPYbV5lg==", + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "find-root": "^1.0.0", - "glob": "^7.0.5", - "ignore": "^5.0.0", - "pkg-config": "^1.1.0", - "run-parallel": "^1.1.2", - "uniq": "^1.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { - "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", - "dev": true + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } } } }, @@ -487,15 +1074,30 @@ "esutils": "^2.0.2" } }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "encodeurl": { @@ -503,15 +1105,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -558,9 +1151,9 @@ "dev": true }, "eslint": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.2.1.tgz", - "integrity": "sha512-ES7BzEzr0Q6m5TK9i+/iTpKjclXitOdDK4vT07OqbkBT2/VcN/gO9EL1C4HlK3TAOXYv2ItcmbVR9jO1MR0fJg==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.7.2.tgz", + "integrity": "sha512-qMlSWJaCSxDFr8fBPvJM9kJwbazrhNcBU3+DszDW1OlEwKBBRWsJc7NJFelvwQpanHCR14cOLD41x8Eqvo3Nng==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -570,19 +1163,19 @@ "debug": "^4.0.1", "doctrine": "^3.0.0", "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.2", + "eslint-utils": "^1.4.3", "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.0", + "espree": "^6.1.2", "esquery": "^1.0.1", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", - "globals": "^11.7.0", + "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^6.4.1", + "inquirer": "^7.0.0", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", @@ -591,7 +1184,7 @@ "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.2", + "optionator": "^0.8.3", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^6.1.2", @@ -617,46 +1210,18 @@ "ms": "^2.1.1" } }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "eslint-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", - "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.0.0" - } - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -680,153 +1245,6 @@ } } }, - "eslint-config-semistandard": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-semistandard/-/eslint-config-semistandard-15.0.0.tgz", - "integrity": "sha512-volIMnosUvzyxGkYUA5QvwkahZZLeUx7wcS0+7QumPn+MMEBbV6P7BY1yukamMst0w3Et3QZlCjQEwQ8tQ6nug==", - "dev": true - }, - "eslint-config-standard": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.0.0.tgz", - "integrity": "sha512-bV6e2LFvJEetrLjVAy4KWPOUsIhPWr040c649MigTPR6yUtaGuOt6CEAyNeez2lRiC+2+vjGWa02byjs25EB3A==", - "dev": true - }, - "eslint-config-standard-jsx": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.0.0.tgz", - "integrity": "sha512-Abs/WP+638KfkHI1J961yAztpMhYFvEMTqD4GMUZObAO9yOmLaQZlJY6xke1ty1+zhY4G58AuvHo3ht6avAEVQ==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.5.0" - } - }, - "eslint-module-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz", - "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^2.0.0" - } - }, - "eslint-plugin-es": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz", - "integrity": "sha512-XfFmgFdIUDgvaRAlaXUkxrRg5JSADoRC8IkKLc/cISeR3yHVMefFHQZpcyXXEUUPHfy5DwviBcrfqlyqEwlQVw==", - "dev": true, - "requires": { - "eslint-utils": "^1.3.0", - "regexpp": "^2.0.1" - } - }, - "eslint-plugin-import": { - "version": "2.18.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", - "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.0", - "read-pkg-up": "^2.0.0", - "resolve": "^1.11.0" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - } - } - }, - "eslint-plugin-node": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-9.1.0.tgz", - "integrity": "sha512-ZwQYGm6EoV2cfLpE1wxJWsfnKUIXfM/KM09/TlorkukgCAwmkgajEJnPCmyzoFPQQkmvo5DrW/nyKutNIw36Mw==", - "dev": true, - "requires": { - "eslint-plugin-es": "^1.4.0", - "eslint-utils": "^1.3.1", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-promise": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", - "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", - "dev": true - }, - "eslint-plugin-react": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", - "integrity": "sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.1.0", - "object.entries": "^1.1.0", - "object.fromentries": "^2.0.0", - "object.values": "^1.1.0", - "prop-types": "^15.7.2", - "resolve": "^1.10.1" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - } - } - }, - "eslint-plugin-standard": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", - "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", - "dev": true - }, "eslint-scope": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", @@ -838,12 +1256,12 @@ } }, "eslint-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", - "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.0.0" + "eslint-visitor-keys": "^1.1.0" } }, "eslint-visitor-keys": { @@ -853,13 +1271,13 @@ "dev": true }, "espree": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.0.tgz", - "integrity": "sha512-boA7CHRLlVWUSg3iL5Kmlt/xT3Q+sXnKoRYYzj1YeM10A76TEJBbotV5pKbnK42hEUIr121zTv+QLRM5LsCPXQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", + "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", "dev": true, "requires": { - "acorn": "^7.0.0", - "acorn-jsx": "^5.0.0", + "acorn": "^7.1.0", + "acorn-jsx": "^5.1.0", "eslint-visitor-keys": "^1.1.0" } }, @@ -894,9 +1312,9 @@ "dev": true }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "etag": { @@ -904,19 +1322,39 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "express": { @@ -956,6 +1394,27 @@ "vary": "~1.1.2" } }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -965,15 +1424,69 @@ "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -985,9 +1498,9 @@ "dev": true }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fast-levenshtein": { @@ -997,9 +1510,9 @@ "dev": true }, "figures": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.0.0.tgz", - "integrity": "sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", + "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -1014,6 +1527,29 @@ "flat-cache": "^2.0.1" } }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -1028,12 +1564,6 @@ "unpipe": "~1.0.0" } }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -1061,6 +1591,17 @@ "flatted": "^2.0.0", "rimraf": "2.6.3", "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "flatted": { @@ -1069,11 +1610,26 @@ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -1085,6 +1641,554 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true + } + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1109,21 +2213,12 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, - "get-stdin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", - "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -1139,19 +2234,58 @@ } }, "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", + "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + } + } }, "graceful-fs": { "version": "4.2.2", @@ -1186,6 +2320,44 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1210,22 +2382,43 @@ "toidentifier": "1.0.0" } }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, "import-fresh": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", - "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -1247,10 +2440,16 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "inquirer": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.1.tgz", - "integrity": "sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.0.tgz", + "integrity": "sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -1269,15 +2468,9 @@ }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "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==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "is-fullwidth-code-point": { @@ -1286,21 +2479,26 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "string-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz", - "integrity": "sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^5.2.0" + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } } }, "strip-ansi": { @@ -1310,31 +2508,68 @@ "dev": true, "requires": { "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } } } } }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, "ipaddr.js": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", "dev": true }, "is-callable": { @@ -1343,12 +2578,72 @@ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "is-date-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1370,12 +2665,84 @@ "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true + }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -1385,6 +2752,12 @@ "has": "^1.0.1" } }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -1400,6 +2773,12 @@ "has-symbols": "^1.0.0" } }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -1412,6 +2791,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1446,29 +2831,25 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "jsx-ast-utils": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz", - "integrity": "sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "object.assign": "^4.1.0" - } - }, "just-extend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { - "invert-kv": "^2.0.0" + "package-json": "^4.0.0" } }, "levn": { @@ -1482,14 +2863,14 @@ } }, "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", + "parse-json": "^4.0.0", + "pify": "^3.0.0", "strip-bom": "^3.0.0" } }, @@ -1509,6 +2890,12 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -1524,22 +2911,50 @@ "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", "dev": true }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { - "p-defer": "^1.0.0" + "pify": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" } }, "media-typer": { @@ -1547,16 +2962,11 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true }, "merge-descriptors": { "version": "1.0.1", @@ -1568,6 +2978,27 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -1606,6 +3037,27 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -1624,9 +3076,9 @@ } }, "mocha": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", - "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.2.tgz", + "integrity": "sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A==", "dev": true, "requires": { "ansi-colors": "3.2.3", @@ -1649,9 +3101,9 @@ "supports-color": "6.0.0", "which": "1.3.1", "wide-align": "1.1.3", - "yargs": "13.2.2", - "yargs-parser": "13.0.0", - "yargs-unparser": "1.5.0" + "yargs": "13.3.0", + "yargs-parser": "13.1.1", + "yargs-unparser": "1.6.0" }, "dependencies": { "debug": { @@ -1671,6 +3123,15 @@ } } }, + "mock-socket": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-8.0.5.tgz", + "integrity": "sha512-dE2EbcxJKQCeYLZSsI7BAiMZCe/bHbJ2LHb5aGwUuDmfoOINEJ8QI6qYJ85NHsSNkNa90F3s6onZcmt/+MppFA==", + "dev": true, + "requires": { + "url-parse": "^1.2.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1682,6 +3143,32 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1700,9 +3187,9 @@ "dev": true }, "nise": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.1.tgz", - "integrity": "sha512-edFWm0fsFG2n318rfEnKlTZTkjlbVOFF9XIA+fj+Ed+Qz1laYW2lobwavWoMzGrYDHH1EpiNJgDfvGnkZztR/g==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.2.tgz", + "integrity": "sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA==", "dev": true, "requires": { "@sinonjs/formatio": "^3.2.1", @@ -1719,9 +3206,9 @@ "dev": true }, "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, "requires": { "isarray": "0.0.1" @@ -1739,6 +3226,50 @@ "semver": "^5.7.0" } }, + "nodemon": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.1.tgz", + "integrity": "sha512-/DXLzd/GhiaDXXbGId5BzxP1GlsqtMGM9zTmkWrgXtSqjKmGSbLicM/oAy4FR0YWm14jCHRwnR31AHS2dYFHrg==", + "dev": true, + "requires": { + "chokidar": "^2.1.5", + "debug": "^3.1.0", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.6", + "semver": "^5.5.0", + "supports-color": "^5.2.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^2.5.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "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==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1751,6 +3282,29 @@ "validate-npm-package-license": "^3.0.1" } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -1760,23 +3314,69 @@ "path-key": "^2.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-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==", + "dev": true + }, "object-keys": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", "dev": true }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, "object.assign": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", @@ -1789,50 +3389,67 @@ "object-keys": "^1.0.11" } }, - "object.entries": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", - "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "object.fromentries": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", - "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.11.0", - "function-bind": "^1.1.1", - "has": "^1.0.1" - } - }, "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.values": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", - "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", "dev": true, "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0-next.1.tgz", + "integrity": "sha512-7MmGr03N7Rnuid6+wyhD9sHNE2n4tFSwExnU2lQl3lIo2ShXWGePY80zYaoMOmILWv57H0amMjZGHNzzGG70Rw==", + "dev": true, + "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.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + } + }, + "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==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "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==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" } }, "on-finished": { @@ -1871,36 +3488,17 @@ } }, "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, "requires": { "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", + "fast-levenshtein": "~2.0.6", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" + "word-wrap": "~1.2.3" } }, "os-tmpdir": { @@ -1909,24 +3507,12 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, "p-limit": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", @@ -1951,6 +3537,18 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1961,12 +3559,13 @@ } }, "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } }, "parseurl": { @@ -1974,6 +3573,18 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -1986,6 +3597,12 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -2004,12 +3621,12 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "^2.0.0" + "pify": "^3.0.0" } }, "pathval": { @@ -2018,123 +3635,23 @@ "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "pidtree": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", + "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", "dev": true }, - "pkg-conf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", - "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "load-json-file": "^5.2.0" - }, - "dependencies": { - "load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true - } - } + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true }, - "pkg-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", - "dev": true, - "requires": { - "debug-log": "^1.0.0", - "find-root": "^1.0.0", - "xtend": "^4.0.1" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - } - } + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true }, "prelude-ls": { "version": "1.1.2", @@ -2142,23 +3659,24 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, "proxy-addr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", @@ -2168,15 +3686,17 @@ "ipaddr.js": "1.9.0" } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "pstree.remy": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", + "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==", + "dev": true }, "punycode": { "version": "2.1.1", @@ -2189,6 +3709,12 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2215,82 +3741,114 @@ } } }, - "react-is": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", - "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } } }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + }, + "registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, "require-directory": { @@ -2305,6 +3863,12 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, "resolve": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", @@ -2320,6 +3884,12 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -2330,10 +3900,16 @@ "signal-exit": "^3.0.2" } }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", "dev": true, "requires": { "glob": "^7.1.3" @@ -2348,16 +3924,10 @@ "is-promise": "^2.1.0" } }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, "rxjs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", - "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -2368,124 +3938,35 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "semistandard": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/semistandard/-/semistandard-14.0.1.tgz", - "integrity": "sha512-/zLjilCminmmEmYkK6/Z2RYKijQ2NDG9V0DMvPG9mHBdlv2jtu/EEFTdiz/rishopglyuHCOEjkSYI4hD8poJA==", - "dev": true, - "requires": { - "eslint": "~6.1.0", - "eslint-config-semistandard": "~15.0.0", - "eslint-config-standard": "~14.0.0", - "eslint-config-standard-jsx": "~8.0.0", - "eslint-plugin-import": "~2.18.0", - "eslint-plugin-node": "~9.1.0", - "eslint-plugin-promise": "~4.2.1", - "eslint-plugin-react": "~7.14.2", - "eslint-plugin-standard": "~4.0.0", - "standard-engine": "^12.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "eslint": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.1.0.tgz", - "integrity": "sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^6.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.4.1", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", - "dev": true - } - } - }, "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -2530,6 +4011,29 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", @@ -2550,6 +4054,12 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shell-quote": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.1.tgz", + "integrity": "sha512-2kUqeAGnMAu6YrTPX4E3LfxacH9gKljzVjlkUeSqY0soGwK4KLl7TURXCem712tkhBCeeaFP9QK4dKn88s3Icg==", + "dev": true + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -2557,17 +4067,17 @@ "dev": true }, "sinon": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.4.1.tgz", - "integrity": "sha512-7s9buHGHN/jqoy/v4bJgmt0m1XEkCEd/tqdHXumpBp0JSujaT4Ng84JU5wDdK4E85ZMq78NuDe0I3NAqXY8TFg==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", + "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", "dev": true, "requires": { "@sinonjs/commons": "^1.4.0", "@sinonjs/formatio": "^3.2.1", - "@sinonjs/samsam": "^3.3.2", + "@sinonjs/samsam": "^3.3.3", "diff": "^3.5.0", "lolex": "^4.2.0", - "nise": "^1.5.1", + "nise": "^1.5.2", "supports-color": "^5.5.0" }, "dependencies": { @@ -2593,6 +4103,160 @@ "is-fullwidth-code-point": "^2.0.0" } }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", @@ -2625,29 +4289,39 @@ "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "standard-engine": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-12.0.0.tgz", - "integrity": "sha512-gJIIRb0LpL7AHyGbN9+hJ4UJns37lxmNTnMGRLC8CFrzQ+oB/K60IQjKNgPBCB2VP60Ypm6f8DFXvhVWdBOO+g==", + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "deglob": "^4.0.0", - "get-stdin": "^7.0.0", - "minimist": "^1.1.0", - "pkg-conf": "^3.1.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } } } }, @@ -2666,6 +4340,46 @@ "strip-ansi": "^4.0.0" } }, + "string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -2720,10 +4434,10 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "string-width": { @@ -2748,6 +4462,49 @@ } } }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "dev": true, + "requires": { + "execa": "^0.7.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2760,6 +4517,12 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -2769,17 +4532,115 @@ "os-tmpdir": "~1.0.2" } }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + }, + "dependencies": { + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1" + } + } + } + }, + "ts-node": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz", + "integrity": "sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + } + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -2796,9 +4657,9 @@ "dev": true }, "type-fest": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", - "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, "type-is": { @@ -2810,17 +4671,117 @@ "mime-types": "~2.1.24" } }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "typescript": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", + "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", "dev": true }, + "undefsafe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", + "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", + "dev": true, + "requires": { + "debug": "^2.2.0" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", + "dev": true + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", + "dev": true + }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "dev": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -2830,11 +4791,53 @@ "punycode": "^2.1.0" } }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + }, "v8-compile-cache": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", @@ -2880,54 +4883,67 @@ "string-width": "^1.0.2 || 2" } }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "dev": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" }, "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" }, "dependencies": { "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^4.1.0" } } } @@ -2947,6 +4963,17 @@ "mkdirp": "^0.5.1" } }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, "ws": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz", @@ -2955,10 +4982,10 @@ "async-limiter": "^1.0.0" } }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, "y18n": { @@ -2967,23 +4994,28 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, "yargs": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", - "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", "dev": true, "requires": { - "cliui": "^4.0.0", + "cliui": "^5.0.0", "find-up": "^3.0.0", "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", "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.0.0" + "yargs-parser": "^13.1.1" }, "dependencies": { "ansi-regex": { @@ -2992,6 +5024,12 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -3015,9 +5053,9 @@ } }, "yargs-parser": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", - "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -3025,59 +5063,21 @@ } }, "yargs-unparser": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", - "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", "dev": true, "requires": { "flat": "^4.1.0", - "lodash": "^4.17.11", - "yargs": "^12.0.5" - }, - "dependencies": { - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "lodash": "^4.17.15", + "yargs": "^13.3.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 9a5ea99..65b2d29 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "peer", "version": "0.3.0", "description": "PeerJS server component", - "main": "src/index.js", + "main": "dist/peerjs.server.min.js", "bin": { "peerjs": "./bin/peerjs" }, @@ -13,22 +13,47 @@ "author": "Michelle Bu, Eric Zhang", "license": "MIT", "scripts": { - "test": "eslint . && mocha \"test/**/*.js\"", - "start": "bin/peerjs --port ${PORT:=9000}" + "build": "tsc", + "clean": "rimraf ./dist", + "lint": "eslint --ext .js,.ts .", + "tsc": "tsc", + "prebuild": "npm run lint", + "test": "npm run lint && mocha -r ts-node/register \"test/**/*\"", + "start": "bin/peerjs --port ${PORT:=9000}", + "dev:start": "npm-run-all build start", + "dev": "nodemon --watch src -e ts --exec npm run dev:start" + }, + "release": { + "branch": "master" }, "dependencies": { - "body-parser": "^1.19.0", - "cors": "~2.8.4", - "express": "^4.17.1", - "optimist": "~0.6.1", - "ws": "^7.1.2" + "@types/cors": "2.8.6", + "@types/express": "4.17.1", + "@types/ws": "6.0.4", + "body-parser": "1.19.0", + "cors": "2.8.4", + "express": "4.17.1", + "optimist": "0.6.1", + "uuid": "3.3.3", + "ws": "7.1.2" }, "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.7", + "@types/node": "^10.14.16", + "@types/uuid": "3.4.6", + "@typescript-eslint/eslint-plugin": "^2.11.0", + "@typescript-eslint/parser": "^2.11.0", "chai": "^4.2.0", - "eslint": "^6.2.1", - "mocha": "^6.2.0", - "semistandard": "^14.0.1", - "sinon": "^7.4.1" + "eslint": "^6.7.2", + "mocha": "^6.2.2", + "mock-socket": "8.0.5", + "nodemon": "1.19.1", + "npm-run-all": "4.1.5", + "rimraf": "3.0.0", + "sinon": "7.5.0", + "ts-node": "8.5.4", + "typescript": "3.7.3" }, "engines": { "node": ">=10" diff --git a/src/api/index.js b/src/api/index.js deleted file mode 100644 index 86f3a07..0000000 --- a/src/api/index.js +++ /dev/null @@ -1,23 +0,0 @@ -const express = require('express'); -const cors = require('cors'); -const bodyParser = require('body-parser'); -const publicContent = require('../../app.json'); - -module.exports = ({ config, realm, messageHandler }) => { - const authMiddleware = require('./middleware/auth')({ config, realm }); - - const app = express.Router(); - - const jsonParser = bodyParser.json(); - - app.use(cors()); - - app.get('/', (req, res) => { - res.send(publicContent); - }); - - app.use('/:key', require('./v1/public')({ config, realm })); - app.use('/:key/:id/:token', authMiddleware, jsonParser, require('./v1/calls')({ realm, messageHandler })); - - return app; -}; diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..257bf24 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,33 @@ +import bodyParser from "body-parser"; +import cors from "cors"; +import express from "express"; +import publicContent from "../../app.json"; +import { IConfig } from "../config"; +import { IMessageHandler } from "../messageHandler"; +import { IRealm } from "../models/realm"; +import { AuthMiddleware } from "./middleware/auth"; +import CallsApi from "./v1/calls"; +import PublicApi from "./v1/public"; + +export const Api = ({ config, realm, messageHandler }: { + config: IConfig; + realm: IRealm; + messageHandler: IMessageHandler; +}): express.Router => { + const authMiddleware = new AuthMiddleware(config, realm); + + const app = express.Router(); + + const jsonParser = bodyParser.json(); + + app.use(cors()); + + app.get("/", (_, res) => { + res.send(publicContent); + }); + + app.use("/:key", PublicApi({ config, realm })); + app.use("/:key/:id/:token", authMiddleware.handle, jsonParser, CallsApi({ realm, messageHandler })); + + return app; +}; diff --git a/src/api/middleware/auth/index.js b/src/api/middleware/auth/index.js deleted file mode 100644 index 498bb5f..0000000 --- a/src/api/middleware/auth/index.js +++ /dev/null @@ -1,25 +0,0 @@ -const { Errors } = require('../../../enums'); - -module.exports = ({ config, realm }) => (req, res, next) => { - const { id, token, key } = req.params; - - if (key !== config.key) { - return res.status(401).send(Errors.INVALID_KEY); - } - - if (!id) { - return res.sendStatus(401); - } - - const client = realm.getClientById(id); - - if (!client) { - return res.sendStatus(401); - } - - if (client.getToken() && token !== client.getToken()) { - return res.status(401).send(Errors.INVALID_TOKEN); - } - - next(); -}; diff --git a/src/api/middleware/auth/index.ts b/src/api/middleware/auth/index.ts new file mode 100644 index 0000000..c46e206 --- /dev/null +++ b/src/api/middleware/auth/index.ts @@ -0,0 +1,35 @@ +import express from "express"; +import { IConfig } from "../../../config"; +import { Errors } from "../../../enums"; +import { IRealm } from "../../../models/realm"; +import { IMiddleware } from "../middleware"; + +export class AuthMiddleware implements IMiddleware { + + constructor(private readonly config: IConfig, private readonly realm: IRealm) { } + + public handle(req: express.Request, res: express.Response, next: express.NextFunction): any { + const { id, token, key } = req.params; + + if (key !== this.config.key) { + return res.status(401).send(Errors.INVALID_KEY); + } + + if (!id) { + return res.sendStatus(401); + } + + const client = this.realm.getClientById(id); + + if (!client) { + return res.sendStatus(401); + } + + if (client.getToken() && token !== client.getToken()) { + return res.status(401).send(Errors.INVALID_TOKEN); + } + + next(); + } + +} diff --git a/src/api/middleware/middleware.ts b/src/api/middleware/middleware.ts new file mode 100644 index 0000000..9c28f9d --- /dev/null +++ b/src/api/middleware/middleware.ts @@ -0,0 +1,5 @@ +import express from "express"; + +export interface IMiddleware { + handle(req: express.Request, res: express.Response, next: express.NextFunction): any; +} diff --git a/src/api/v1/calls/index.js b/src/api/v1/calls/index.js deleted file mode 100644 index d022cda..0000000 --- a/src/api/v1/calls/index.js +++ /dev/null @@ -1,36 +0,0 @@ -const express = require('express'); - -module.exports = ({ realm, messageHandler }) => { - const app = express.Router(); - - const handle = (req, res, next) => { - const { id } = req.params; - - if (!id) return next(); - - const client = realm.getClientById(id); - - const { type, dst, payload } = req.body; - - const message = { - type, - src: id, - dst, - payload - }; - - messageHandler(client, message); - - res.sendStatus(200); - }; - - app.post('/offer', handle); - - app.post('/candidate', handle); - - app.post('/answer', handle); - - app.post('/leave', handle); - - return app; -}; diff --git a/src/api/v1/calls/index.ts b/src/api/v1/calls/index.ts new file mode 100644 index 0000000..ea42f8c --- /dev/null +++ b/src/api/v1/calls/index.ts @@ -0,0 +1,40 @@ +import express from "express"; +import { IMessageHandler } from "../../../messageHandler"; +import { IMessage } from "../../../models/message"; +import { IRealm } from "../../../models/realm"; + +export default ({ realm, messageHandler }: { realm: IRealm, messageHandler: IMessageHandler; }): express.Router => { + const app = express.Router(); + + const handle = (req: express.Request, res: express.Response, next: express.NextFunction): any => { + const { id } = req.params; + + if (!id) return next(); + + const client = realm.getClientById(id); + + if (!client) { + throw new Error(`client not found:${id}`); + } + + const { type, dst, payload } = req.body; + + const message: IMessage = { + type, + src: id, + dst, + payload + }; + + messageHandler.handle(client, message); + + res.sendStatus(200); + }; + + app.post("/offer", handle); + app.post("/candidate", handle); + app.post("/answer", handle); + app.post("/leave", handle); + + return app; +}; diff --git a/src/api/v1/public/index.js b/src/api/v1/public/index.ts similarity index 52% rename from src/api/v1/public/index.js rename to src/api/v1/public/index.ts index f500aa1..fc22229 100644 --- a/src/api/v1/public/index.js +++ b/src/api/v1/public/index.ts @@ -1,16 +1,20 @@ -const express = require('express'); +import express from "express"; +import { IConfig } from "../../../config"; +import { IRealm } from "../../../models/realm"; -module.exports = ({ config, realm }) => { +export default ({ config, realm }: { + config: IConfig, realm: IRealm +}): express.Router => { const app = express.Router(); // Retrieve guaranteed random ID. - app.get('/id', (req, res) => { - res.contentType = 'text/html'; + app.get("/id", (_, res: express.Response) => { + res.contentType("html"); res.send(realm.generateClientId(config.genRandomId)); }); // Get a list of all peers for a key, enabled by the `allowDiscovery` flag. - app.get('/peers', (req, res) => { + app.get("/peers", (_, res: express.Response) => { if (config.allow_discovery) { const clientsIds = realm.getClientsIds(); diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..3683e73 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,33 @@ +export interface IConfig { + readonly port: number; + readonly expire_timeout: number; + readonly alive_timeout: number; + readonly key: string; + readonly path: string; + readonly concurrent_limit: number; + readonly allow_discovery: boolean; + readonly proxied: boolean | string; + readonly cleanup_out_msgs: number; + readonly ssl?: { + key: string; + cert: string; + }; +} + +const defaultConfig: IConfig = { + port: 9000, + expire_timeout: 5000, + alive_timeout: 60000, + key: "peerjs", + path: "/myapp", + concurrent_limit: 5000, + allow_discovery: false, + proxied: false, + cleanup_out_msgs: 1000, + ssl: { + key: "", + cert: "" + } +}; + +export default defaultConfig; diff --git a/src/enums.js b/src/enums.js deleted file mode 100644 index 93044a4..0000000 --- a/src/enums.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports.Errors = { - INVALID_KEY: 'Invalid key provided', - INVALID_TOKEN: 'Invalid token provided', - INVALID_WS_PARAMETERS: 'No id, token, or key supplied to websocket server', - CONNECTION_LIMIT_EXCEED: 'Server has reached its concurrent user limit' -}; - -module.exports.MessageType = { - OPEN: 'OPEN', - LEAVE: 'LEAVE', - CANDIDATE: 'CANDIDATE', - OFFER: 'OFFER', - ANSWER: 'ANSWER', - EXPIRE: 'EXPIRE', - HEARTBEAT: 'HEARTBEAT', - ID_TAKEN: 'ID-TAKEN', - ERROR: 'ERROR' -}; diff --git a/src/enums.ts b/src/enums.ts new file mode 100644 index 0000000..008c7e6 --- /dev/null +++ b/src/enums.ts @@ -0,0 +1,18 @@ +export enum Errors { + INVALID_KEY = "Invalid key provided", + INVALID_TOKEN = "Invalid token provided", + INVALID_WS_PARAMETERS = "No id, token, or key supplied to websocket server", + CONNECTION_LIMIT_EXCEED = "Server has reached its concurrent user limit" +} + +export enum MessageType { + OPEN = "OPEN", + LEAVE = "LEAVE", + CANDIDATE = "CANDIDATE", + OFFER = "OFFER", + ANSWER = "ANSWER", + EXPIRE = "EXPIRE", + HEARTBEAT = "HEARTBEAT", + ID_TAKEN = "ID-TAKEN", + ERROR = "ERROR" +} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index ea3d9e0..0000000 --- a/src/index.js +++ /dev/null @@ -1,134 +0,0 @@ -const express = require('express'); -const http = require('http'); -const https = require('https'); - -const defaultConfig = require('../config'); -const WebSocketServer = require('./services/webSocketServer'); -const Realm = require('./models/realm'); - -const init = ({ app, server, options }) => { - const config = options; - const realm = new Realm(); - const messageHandler = require('./messageHandler')({ realm }); - const api = require('./api')({ config, realm, messageHandler }); - - const { startMessagesExpiration } = require('./services/messagesExpire')({ realm, config, messageHandler }); - const checkBrokenConnections = require('./services/checkBrokenConnections')({ - realm, config, onClose: (client) => { - app.emit('disconnect', client); - } - }); - - app.use(options.path, api); - - const wss = new WebSocketServer({ - server, - realm, - config: { - ...config, - } - }); - - wss.on('connection', client => { - const messageQueue = realm.getMessageQueueById(client.getId()); - - if (messageQueue) { - let message; - // eslint-disable-next-line no-cond-assign - while (message = messageQueue.readMessage()) { - messageHandler(client, message); - } - realm.clearMessageQueue(client.getId()); - } - - app.emit('connection', client); - }); - - wss.on('message', (client, message) => { - app.emit('message', client, message); - messageHandler(client, message); - }); - - wss.on('close', client => { - app.emit('disconnect', client); - }); - - wss.on('error', error => { - app.emit('error', error); - }); - - startMessagesExpiration(); - - checkBrokenConnections.start(); -}; - -function ExpressPeerServer(server, options) { - const app = express(); - - options = { - ...defaultConfig, - ...options - }; - - if (options.proxied) { - app.set('trust proxy', options.proxied === 'false' ? false : options.proxied); - } - - app.on('mount', () => { - if (!server) { - throw new Error('Server is not passed to constructor - ' + - 'can\'t start PeerServer'); - } - - init({ app, server, options }); - }); - - return app; -} - -function PeerServer(options = {}, callback) { - const app = express(); - - options = { - ...defaultConfig, - ...options - }; - - let path = options.path; - const port = options.port; - - if (path[0] !== '/') { - path = '/' + path; - } - - if (path[path.length - 1] !== '/') { - path += '/'; - } - - let server; - - if (options.ssl && options.ssl.key && options.ssl.cert) { - server = https.createServer(options.ssl, app); - delete options.ssl; - } else { - server = http.createServer(app); - } - - const peerjs = ExpressPeerServer(server, options); - app.use(peerjs); - - if (callback) { - server.listen(port, () => { - callback(server); - }); - } else { - server.listen(port); - } - - return peerjs; -} - -exports = module.exports = { - ExpressPeerServer: ExpressPeerServer, - PeerServer: PeerServer -}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..61f55b3 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,77 @@ +import express from "express"; +import http from "http"; +import https from "https"; +import { Server } from "net"; + +import defaultConfig, { IConfig } from "./config"; +import { createInstance } from "./instance"; + +type Optional = { + [P in keyof T]?: (T[P] | undefined); +}; + +function ExpressPeerServer(server: Server, options?: IConfig) { + const app = express(); + + const newOptions: IConfig = { + ...defaultConfig, + ...options + }; + + if (newOptions.proxied) { + app.set("trust proxy", newOptions.proxied === "false" ? false : !!newOptions.proxied); + } + + app.on("mount", () => { + if (!server) { + throw new Error("Server is not passed to constructor - " + + "can't start PeerServer"); + } + + createInstance({ app, server, options: newOptions }); + }); + + return app; +} + +function PeerServer(options: Optional = {}, callback?: (server: Server) => void) { + const app = express(); + + const newOptions: IConfig = { + ...defaultConfig, + ...options + }; + + let path = newOptions.path; + const port = newOptions.port; + + if (!path.startsWith('/')) { + path = "/" + path; + } + + if (!path.endsWith('/')) { + path += "/"; + } + + let server: Server; + + if (newOptions.ssl && newOptions.ssl.key && newOptions.ssl.cert) { + server = https.createServer(options.ssl!, app); + // @ts-ignore + delete newOptions.ssl; + } else { + server = http.createServer(app); + } + + const peerjs = ExpressPeerServer(server, newOptions); + app.use(peerjs); + + server.listen(port, () => callback?.(server)); + + return peerjs; +} + +export { + ExpressPeerServer, + PeerServer +}; diff --git a/src/instance.ts b/src/instance.ts new file mode 100644 index 0000000..cd8cc66 --- /dev/null +++ b/src/instance.ts @@ -0,0 +1,71 @@ +import express from "express"; +import { Server } from "net"; +import { IClient } from "./models/client"; +import { IMessage } from "./models/message"; +import { Realm } from "./models/realm"; +import { IRealm } from "./models/realm"; +import { CheckBrokenConnections } from "./services/checkBrokenConnections"; +import { IMessagesExpire, MessagesExpire } from "./services/messagesExpire"; +import { IWebSocketServer, WebSocketServer } from "./services/webSocketServer"; +import { MessageHandler } from "./messageHandler"; +import { Api } from "./api"; +import { IConfig } from "./config"; + +export const createInstance = ({ app, server, options }: { + app: express.Application, + server: Server, + options: IConfig; +}): void => { + const config = options; + const realm: IRealm = new Realm(); + const messageHandler = new MessageHandler(realm); + + const api = Api({ config, realm, messageHandler }); + const messagesExpire: IMessagesExpire = new MessagesExpire({ realm, config, messageHandler }); + const checkBrokenConnections = new CheckBrokenConnections({ + realm, + config, + onClose: client => { + app.emit("disconnect", client); + } + }); + + app.use(options.path, api); + + const wss: IWebSocketServer = new WebSocketServer({ + server, + realm, + config + }); + + wss.on("connection", (client: IClient) => { + const messageQueue = realm.getMessageQueueById(client.getId()); + + if (messageQueue) { + let message: IMessage | undefined; + + while (message = messageQueue.readMessage()) { + messageHandler.handle(client, message); + } + realm.clearMessageQueue(client.getId()); + } + + app.emit("connection", client); + }); + + wss.on("message", (client: IClient, message: IMessage) => { + app.emit("message", client, message); + messageHandler.handle(client, message); + }); + + wss.on("close", (client: IClient) => { + app.emit("disconnect", client); + }); + + wss.on("error", (error: Error) => { + app.emit("error", error); + }); + + messagesExpire.startMessagesExpiration(); + checkBrokenConnections.start(); +}; \ No newline at end of file diff --git a/src/messageHandler/handler.ts b/src/messageHandler/handler.ts new file mode 100644 index 0000000..4c81fde --- /dev/null +++ b/src/messageHandler/handler.ts @@ -0,0 +1,4 @@ +import { IClient } from "../models/client"; +import { IMessage } from "../models/message"; + +export type Handler = (client: IClient | undefined, message: IMessage) => boolean; diff --git a/src/messageHandler/handlers/heartbeat/index.js b/src/messageHandler/handlers/heartbeat/index.js deleted file mode 100644 index a74b99d..0000000 --- a/src/messageHandler/handlers/heartbeat/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = (client) => { - const nowTime = new Date().getTime(); - client.setLastPing(nowTime); -}; diff --git a/src/messageHandler/handlers/heartbeat/index.ts b/src/messageHandler/handlers/heartbeat/index.ts new file mode 100644 index 0000000..43bf35e --- /dev/null +++ b/src/messageHandler/handlers/heartbeat/index.ts @@ -0,0 +1,10 @@ +import { IClient } from "../../../models/client"; + +export const HeartbeatHandler = (client: IClient | undefined): boolean => { + if (client) { + const nowTime = new Date().getTime(); + client.setLastPing(nowTime); + } + + return true; +}; diff --git a/src/messageHandler/handlers/index.ts b/src/messageHandler/handlers/index.ts new file mode 100644 index 0000000..45095d3 --- /dev/null +++ b/src/messageHandler/handlers/index.ts @@ -0,0 +1,2 @@ +export { HeartbeatHandler } from "./heartbeat"; +export { TransmissionHandler } from "./transmission"; diff --git a/src/messageHandler/handlers/transmission/index.js b/src/messageHandler/handlers/transmission/index.js deleted file mode 100644 index aacdb7f..0000000 --- a/src/messageHandler/handlers/transmission/index.js +++ /dev/null @@ -1,49 +0,0 @@ -const { MessageType } = require('../../../enums'); - -module.exports = ({ realm }) => (client, message) => { - const type = message.type; - const srcId = message.src; - const dstId = message.dst; - - const destinationClient = realm.getClientById(dstId); - - // User is connected! - if (destinationClient) { - try { - if (destinationClient.socket) { - const data = JSON.stringify(message); - - destinationClient.socket.send(data); - } else { - // Neither socket no res available. Peer dead? - throw new Error('Peer dead'); - } - } catch (e) { - // This happens when a peer disconnects without closing connections and - // the associated WebSocket has not closed. - // Tell other side to stop trying. - if (destinationClient.socket) { - destinationClient.socket.close(); - } else { - realm.removeClientById(destinationClient.getId()); - } - - module.exports({ realm })(client, { - type: MessageType.LEAVE, - src: dstId, - dst: srcId - }); - } - } else { - // Wait for this client to connect/reconnect (XHR) for important - // messages. - if (type !== MessageType.LEAVE && type !== MessageType.EXPIRE && dstId) { - realm.addMessageToQueue(dstId, message); - } else if (type === MessageType.LEAVE && !dstId) { - realm.removeClientById(srcId); - } else { - // Unavailable destination specified with message LEAVE or EXPIRE - // Ignore - } - } -}; diff --git a/src/messageHandler/handlers/transmission/index.ts b/src/messageHandler/handlers/transmission/index.ts new file mode 100644 index 0000000..bd31c70 --- /dev/null +++ b/src/messageHandler/handlers/transmission/index.ts @@ -0,0 +1,61 @@ +import { MessageType } from "../../../enums"; +import { IClient } from "../../../models/client"; +import { IMessage } from "../../../models/message"; +import { IRealm } from "../../../models/realm"; + +export const TransmissionHandler = ({ realm }: { realm: IRealm; }): (client: IClient | undefined, message: IMessage) => boolean => { + const handle = (client: IClient | undefined, message: IMessage) => { + const type = message.type; + const srcId = message.src; + const dstId = message.dst; + + const destinationClient = realm.getClientById(dstId); + + // User is connected! + if (destinationClient) { + const socket = destinationClient.getSocket(); + try { + if (socket) { + const data = JSON.stringify(message); + + socket.send(data); + } else { + // Neither socket no res available. Peer dead? + throw new Error("Peer dead"); + } + } catch (e) { + // This happens when a peer disconnects without closing connections and + // the associated WebSocket has not closed. + // Tell other side to stop trying. + if (socket) { + socket.close(); + } else { + realm.removeClientById(destinationClient.getId()); + } + + handle(client, { + type: MessageType.LEAVE, + src: dstId, + dst: srcId + }); + } + } else { + // Wait for this client to connect/reconnect (XHR) for important + // messages. + const ignoredTypes = [MessageType.LEAVE, MessageType.EXPIRE]; + + if (!ignoredTypes.includes(type) && dstId) { + realm.addMessageToQueue(dstId, message); + } else if (type === MessageType.LEAVE && !dstId) { + realm.removeClientById(srcId); + } else { + // Unavailable destination specified with message LEAVE or EXPIRE + // Ignore + } + } + + return true; + }; + + return handle; +}; diff --git a/src/messageHandler/handlersRegistry.ts b/src/messageHandler/handlersRegistry.ts new file mode 100644 index 0000000..def2ac8 --- /dev/null +++ b/src/messageHandler/handlersRegistry.ts @@ -0,0 +1,29 @@ +import { MessageType } from "../enums"; +import { IClient } from "../models/client"; +import { IMessage } from "../models/message"; +import { Handler } from "./handler"; + +export interface IHandlersRegistry { + registerHandler(messageType: MessageType, handler: Handler): void; + handle(client: IClient | undefined, message: IMessage): boolean; +} + +export class HandlersRegistry implements IHandlersRegistry { + private readonly handlers: Map = new Map(); + + public registerHandler(messageType: MessageType, handler: Handler): void { + if (this.handlers.has(messageType)) return; + + this.handlers.set(messageType, handler); + } + + public handle(client: IClient | undefined, message: IMessage): boolean { + const { type } = message; + + const handler = this.handlers.get(type); + + if (!handler) return false; + + return handler(client, message); + } +} diff --git a/src/messageHandler/index.js b/src/messageHandler/index.js deleted file mode 100644 index 447e949..0000000 --- a/src/messageHandler/index.js +++ /dev/null @@ -1,49 +0,0 @@ -const { MessageType } = require('../enums'); - -class MessageHandlers { - constructor() { - this.handlers = {}; - } - - registerHandler(messageType, handler) { - this.handlers[messageType] = handler; - } - - handle(client, message) { - const { type } = message; - - const handler = this.handlers[type]; - - if (!handler) { - return; - } - - handler(client, message); - } -} -module.exports = ({ realm }) => { - const transmissionHandler = require('./handlers/transmission')({ realm }); - const heartbeatHandler = require('./handlers/heartbeat'); - - const messageHandlers = new MessageHandlers(); - - const handleTransmission = (client, message) => { - transmissionHandler(client, { - type: message.type, - src: message.src, - dst: message.dst, - payload: message.payload - }); - }; - - const handleHeartbeat = (client) => heartbeatHandler(client); - - messageHandlers.registerHandler(MessageType.HEARTBEAT, handleHeartbeat); - messageHandlers.registerHandler(MessageType.OFFER, handleTransmission); - messageHandlers.registerHandler(MessageType.ANSWER, handleTransmission); - messageHandlers.registerHandler(MessageType.CANDIDATE, handleTransmission); - messageHandlers.registerHandler(MessageType.LEAVE, handleTransmission); - messageHandlers.registerHandler(MessageType.EXPIRE, handleTransmission); - - return (client, message) => messageHandlers.handle(client, message); -}; diff --git a/src/messageHandler/index.ts b/src/messageHandler/index.ts new file mode 100644 index 0000000..7f6c67a --- /dev/null +++ b/src/messageHandler/index.ts @@ -0,0 +1,40 @@ +import { MessageType } from "../enums"; +import { IClient } from "../models/client"; +import { IMessage } from "../models/message"; +import { IRealm } from "../models/realm"; +import { Handler } from "./handler"; +import { HeartbeatHandler, TransmissionHandler } from "./handlers"; +import { IHandlersRegistry, HandlersRegistry } from "./handlersRegistry"; + +export interface IMessageHandler { + handle(client: IClient | undefined, message: IMessage): boolean; +} + +export class MessageHandler implements IMessageHandler { + constructor(realm: IRealm, private readonly handlersRegistry: IHandlersRegistry = new HandlersRegistry()) { + const transmissionHandler: Handler = TransmissionHandler({ realm }); + const heartbeatHandler: Handler = HeartbeatHandler; + + const handleTransmission: Handler = (client: IClient | undefined, { type, src, dst, payload }: IMessage): boolean => { + return transmissionHandler(client, { + type, + src, + dst, + payload, + }); + }; + + const handleHeartbeat = (client: IClient | undefined, message: IMessage) => heartbeatHandler(client, message); + + this.handlersRegistry.registerHandler(MessageType.HEARTBEAT, handleHeartbeat); + this.handlersRegistry.registerHandler(MessageType.OFFER, handleTransmission); + this.handlersRegistry.registerHandler(MessageType.ANSWER, handleTransmission); + this.handlersRegistry.registerHandler(MessageType.CANDIDATE, handleTransmission); + this.handlersRegistry.registerHandler(MessageType.LEAVE, handleTransmission); + this.handlersRegistry.registerHandler(MessageType.EXPIRE, handleTransmission); + } + + public handle(client: IClient | undefined, message: IMessage): boolean { + return this.handlersRegistry.handle(client, message); + } +} diff --git a/src/models/client.js b/src/models/client.js deleted file mode 100644 index 50cc5ef..0000000 --- a/src/models/client.js +++ /dev/null @@ -1,38 +0,0 @@ -class Client { - constructor({ id, token }) { - this.id = id; - this.token = token; - this.socket = null; - this.lastPing = new Date().getTime(); - } - - getId() { - return this.id; - } - - getToken() { - return this.token; - } - - getSocket() { - return this.socket; - } - - setSocket(socket) { - this.socket = socket; - } - - getLastPing() { - return this.lastPing; - } - - setLastPing(lastPing) { - this.lastPing = lastPing; - } - - send(data) { - this.socket.send(JSON.stringify(data)); - } -} - -module.exports = Client; diff --git a/src/models/client.ts b/src/models/client.ts new file mode 100644 index 0000000..4dee3ca --- /dev/null +++ b/src/models/client.ts @@ -0,0 +1,57 @@ +import { MyWebSocket } from "../services/webSocketServer/webSocket"; + +export interface IClient { + getId(): string; + + getToken(): string; + + getSocket(): MyWebSocket | null; + + setSocket(socket: MyWebSocket | null): void; + + getLastPing(): number; + + setLastPing(lastPing: number): void; + + send(data: any): void; +} + +export class Client implements IClient { + private readonly id: string; + private readonly token: string; + private socket: MyWebSocket | null = null; + private lastPing: number = new Date().getTime(); + + constructor({ id, token }: { id: string, token: string; }) { + this.id = id; + this.token = token; + } + + public getId(): string { + return this.id; + } + + public getToken(): string { + return this.token; + } + + public getSocket(): MyWebSocket | null { + return this.socket; + } + + public setSocket(socket: MyWebSocket | null): void { + this.socket = socket; + } + + public getLastPing(): number { + return this.lastPing; + } + + public setLastPing(lastPing: number): void { + this.lastPing = lastPing; + } + + public send(data: any): void { + this.socket?.send(JSON.stringify(data)); + } +} diff --git a/src/models/message.ts b/src/models/message.ts new file mode 100644 index 0000000..06c9156 --- /dev/null +++ b/src/models/message.ts @@ -0,0 +1,8 @@ +import { MessageType } from "../enums"; + +export interface IMessage { + readonly type: MessageType; + readonly src: string; + readonly dst: string; + readonly payload?: any; +} diff --git a/src/models/messageQueue.js b/src/models/messageQueue.js deleted file mode 100644 index ec5fdd1..0000000 --- a/src/models/messageQueue.js +++ /dev/null @@ -1,30 +0,0 @@ -class MessageQueue { - constructor (id) { - this._id = id; - this._lastReadAt = new Date().getTime(); - this._messages = []; - } - - getLastReadAt () { - return this._lastReadAt; - } - - addMessage (message) { - this._messages.push(message); - } - - readMessage () { - if (this._messages.length > 0) { - this._lastReadAt = new Date().getTime(); - return this._messages.shift(); - } - - return null; - } - - getMessages () { - return this._messages; - } -} - -module.exports = MessageQueue; diff --git a/src/models/messageQueue.ts b/src/models/messageQueue.ts new file mode 100644 index 0000000..da716bb --- /dev/null +++ b/src/models/messageQueue.ts @@ -0,0 +1,37 @@ +import { IMessage } from "./message"; + +export interface IMessageQueue { + getLastReadAt(): number; + + addMessage(message: IMessage): void; + + readMessage(): IMessage | undefined; + + getMessages(): IMessage[]; +} + +export class MessageQueue implements IMessageQueue { + private lastReadAt: number = new Date().getTime(); + private readonly messages: IMessage[] = []; + + public getLastReadAt(): number { + return this.lastReadAt; + } + + public addMessage(message: IMessage): void { + this.messages.push(message); + } + + public readMessage(): IMessage | undefined { + if (this.messages.length > 0) { + this.lastReadAt = new Date().getTime(); + return this.messages.shift()!; + } + + return undefined; + } + + public getMessages(): IMessage[] { + return this.messages; + } +} diff --git a/src/models/realm.js b/src/models/realm.js deleted file mode 100644 index 73af397..0000000 --- a/src/models/realm.js +++ /dev/null @@ -1,64 +0,0 @@ -const MessageQueue = require('./messageQueue'); - -class Realm { - constructor () { - this._clients = new Map(); - this._messageQueues = new Map(); - } - - getClientsIds () { - return [...this._clients.keys()]; - } - - getClientById (clientId) { - return this._clients.get(clientId); - } - - setClient (client, id) { - this._clients.set(id, client); - } - - removeClientById (id) { - const client = this.getClientById(id); - - if (!client) return false; - - this._clients.delete(id); - } - - getMessageQueueById (id) { - return this._messageQueues.get(id); - } - - addMessageToQueue (id, message) { - if (!this.getMessageQueueById(id)) { - this._messageQueues.set(id, new MessageQueue(id)); - } - - this.getMessageQueueById(id).addMessage(message); - } - - clearMessageQueue (id) { - this._messageQueues.delete(id); - } - - generateClientId (_genRandomId) { - const originalGenRandomId = () => { - return (Math.random().toString(36) + '0000000000000000000').substr(2, 16); - } - - const genRandomId = _genRandomId && typeof _genRandomId === 'function' ? - _genRandomId : - originalGenRandomId; - - let clientId = genRandomId(); - - while (this.getClientById(clientId)) { - clientId = genRandomId(); - } - - return clientId; - } -} - -module.exports = Realm; diff --git a/src/models/realm.ts b/src/models/realm.ts new file mode 100644 index 0000000..f910829 --- /dev/null +++ b/src/models/realm.ts @@ -0,0 +1,84 @@ +import uuidv4 from "uuid/v4"; +import { IClient } from "./client"; +import { IMessage } from "./message"; +import { IMessageQueue, MessageQueue } from "./messageQueue"; + +export interface IRealm { + getClientsIds(): string[]; + + getClientById(clientId: string): IClient | undefined; + + getClientsIdsWithQueue(): string[]; + + setClient(client: IClient, id: string): void; + + removeClientById(id: string): boolean; + + getMessageQueueById(id: string): IMessageQueue | undefined; + + addMessageToQueue(id: string, message: IMessage): void; + + clearMessageQueue(id: string): void; + + generateClientId(genRandomId: () => string): string; +} + +export class Realm implements IRealm { + private readonly clients: Map = new Map(); + private readonly messageQueues: Map = new Map(); + + public getClientsIds(): string[] { + return [...this.clients.keys()]; + } + + public getClientById(clientId: string): IClient | undefined { + return this.clients.get(clientId); + } + + public getClientsIdsWithQueue(): string[] { + return [...this.messageQueues.keys()]; + } + + public setClient(client: IClient, id: string): void { + this.clients.set(id, client); + } + + public removeClientById(id: string): boolean { + const client = this.getClientById(id); + + if (!client) return false; + + this.clients.delete(id); + + return true; + } + + public getMessageQueueById(id: string): IMessageQueue | undefined { + return this.messageQueues.get(id); + } + + public addMessageToQueue(id: string, message: IMessage): void { + if (!this.getMessageQueueById(id)) { + this.messageQueues.set(id, new MessageQueue()); + } + + this.getMessageQueueById(id)!.addMessage(message); + } + + public clearMessageQueue(id: string): void { + this.messageQueues.delete(id); + } + + public generateClientId(genRandomId: () => string): string { + + const _genRandomId = genRandomId ? genRandomId : uuidv4; + + let clientId = _genRandomId(); + + while (this.getClientById(clientId)) { + clientId = _genRandomId(); + } + + return clientId; + } +} diff --git a/src/services/checkBrokenConnections/index.js b/src/services/checkBrokenConnections/index.js deleted file mode 100644 index fe66fef..0000000 --- a/src/services/checkBrokenConnections/index.js +++ /dev/null @@ -1,57 +0,0 @@ -const DEFAULT_CHECK_INTERVAL = 300; - -module.exports = ({ realm, config, checkInterval = DEFAULT_CHECK_INTERVAL, onClose = () => { } }) => { - const checkConnections = () => { - const clientsIds = realm.getClientsIds(); - - const now = new Date().getTime(); - const aliveTimeout = config.alive_timeout; - - for (const clientId of clientsIds) { - const client = realm.getClientById(clientId); - const timeSinceLastPing = now - client.getLastPing(); - - if (timeSinceLastPing < aliveTimeout) continue; - - try { - client.getSocket().close(); - // eslint-disable-next-line no-empty - } catch (e) { } finally { - realm.clearMessageQueue(clientId); - realm.removeClientById(clientId); - client.setSocket(null); - - if (onClose) onClose(client); - } - } - }; - - let timeoutId; - - const start = () => { - if (timeoutId) { - clearTimeout(timeoutId); - } - - timeoutId = setTimeout(() => { - checkConnections(); - - timeoutId = null; - - start(); - }, checkInterval); - }; - - const stop = () => { - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = null; - } - }; - - return { - start, - stop, - CHECK_INTERVAL: checkInterval - }; -}; diff --git a/src/services/checkBrokenConnections/index.ts b/src/services/checkBrokenConnections/index.ts new file mode 100644 index 0000000..9ef3c4e --- /dev/null +++ b/src/services/checkBrokenConnections/index.ts @@ -0,0 +1,74 @@ +import { IConfig } from "../../config"; +import { IClient } from "../../models/client"; +import { IRealm } from "../../models/realm"; + +const DEFAULT_CHECK_INTERVAL = 300; + +type CustomConfig = Pick; + +export class CheckBrokenConnections { + + public readonly checkInterval: number; + private timeoutId: NodeJS.Timeout | null = null; + private readonly realm: IRealm; + private readonly config: CustomConfig; + private readonly onClose?: (client: IClient) => void; + + constructor({ realm, config, checkInterval = DEFAULT_CHECK_INTERVAL, onClose }: { + realm: IRealm, + config: CustomConfig, + checkInterval?: number, + onClose?: (client: IClient) => void; + }) { + this.realm = realm; + this.config = config; + this.onClose = onClose; + this.checkInterval = checkInterval; + } + + public start(): void { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + + this.timeoutId = setTimeout(() => { + this.checkConnections(); + + this.timeoutId = null; + + this.start(); + }, this.checkInterval); + } + + public stop(): void { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + this.timeoutId = null; + } + } + + private checkConnections(): void { + const clientsIds = this.realm.getClientsIds(); + + const now = new Date().getTime(); + const { alive_timeout: aliveTimeout } = this.config; + + for (const clientId of clientsIds) { + const client = this.realm.getClientById(clientId)!; + const timeSinceLastPing = now - client.getLastPing(); + + if (timeSinceLastPing < aliveTimeout) continue; + + try { + client.getSocket()?.close(); + } finally { + this.realm.clearMessageQueue(clientId); + this.realm.removeClientById(clientId); + + client.setSocket(null); + + this.onClose?.(client); + } + } + } +} diff --git a/src/services/messagesExpire/index.js b/src/services/messagesExpire/index.js deleted file mode 100644 index b4d2b8c..0000000 --- a/src/services/messagesExpire/index.js +++ /dev/null @@ -1,63 +0,0 @@ -const { MessageType } = require('../../enums'); - -module.exports = ({ realm, config, messageHandler }) => { - const pruneOutstanding = () => { - const destinationClientsIds = realm._messageQueues.keys(); - - const now = new Date().getTime(); - const maxDiff = config.expire_timeout; - - const seen = {}; - - for (const destinationClientId of destinationClientsIds) { - const messageQueue = realm.getMessageQueueById(destinationClientId); - const lastReadDiff = now - messageQueue.getLastReadAt(); - - if (lastReadDiff < maxDiff) continue; - - const messages = messageQueue.getMessages(); - - for (const message of messages) { - if (!seen[message.src]) { - messageHandler(null, { - type: MessageType.EXPIRE, - src: message.dst, - dst: message.src - }); - seen[message.src] = true; - } - } - - realm.clearMessageQueue(destinationClientId); - } - }; - - let timeoutId; - - const startMessagesExpiration = () => { - if (timeoutId) { - clearTimeout(timeoutId); - } - - // Clean up outstanding messages - timeoutId = setTimeout(() => { - pruneOutstanding(); - - timeoutId = null; - - startMessagesExpiration(); - }, config.cleanup_out_msgs); - }; - - const stopMessagesExpiration = () => { - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = null; - } - }; - - return { - startMessagesExpiration, - stopMessagesExpiration - }; -}; diff --git a/src/services/messagesExpire/index.ts b/src/services/messagesExpire/index.ts new file mode 100644 index 0000000..cf94a38 --- /dev/null +++ b/src/services/messagesExpire/index.ts @@ -0,0 +1,83 @@ +import { IConfig } from "../../config"; +import { MessageType } from "../../enums"; +import { IMessageHandler } from "../../messageHandler"; +import { IRealm } from "../../models/realm"; + +export interface IMessagesExpire { + startMessagesExpiration(): void; + stopMessagesExpiration(): void; +} + +type CustomConfig = Pick; + +export class MessagesExpire implements IMessagesExpire { + private readonly realm: IRealm; + private readonly config: CustomConfig; + private readonly messageHandler: IMessageHandler; + + private timeoutId: NodeJS.Timeout | null = null; + + constructor({ realm, config, messageHandler }: { + realm: IRealm; + config: CustomConfig; + messageHandler: IMessageHandler; + }) { + this.realm = realm; + this.config = config; + this.messageHandler = messageHandler; + } + + public startMessagesExpiration(): void { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + + // Clean up outstanding messages + this.timeoutId = setTimeout(() => { + this.pruneOutstanding(); + + this.timeoutId = null; + + this.startMessagesExpiration(); + }, this.config.cleanup_out_msgs); + } + + public stopMessagesExpiration(): void { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + this.timeoutId = null; + } + } + + private pruneOutstanding(): void { + const destinationClientsIds = this.realm.getClientsIdsWithQueue(); + + const now = new Date().getTime(); + const maxDiff = this.config.expire_timeout; + + const seen: Record = {}; + + for (const destinationClientId of destinationClientsIds) { + const messageQueue = this.realm.getMessageQueueById(destinationClientId)!; + const lastReadDiff = now - messageQueue.getLastReadAt(); + + if (lastReadDiff < maxDiff) continue; + + const messages = messageQueue.getMessages(); + + for (const message of messages) { + if (!seen[message.src]) { + this.messageHandler.handle(undefined, { + type: MessageType.EXPIRE, + src: message.dst, + dst: message.src + }); + + seen[message.src] = true; + } + } + + this.realm.clearMessageQueue(destinationClientId); + } + } +} diff --git a/src/services/webSocketServer/index.js b/src/services/webSocketServer/index.js deleted file mode 100644 index bfe1b6c..0000000 --- a/src/services/webSocketServer/index.js +++ /dev/null @@ -1,114 +0,0 @@ -const WSS = require('ws').Server; -const url = require('url'); -const EventEmitter = require('events'); -const { MessageType, Errors } = require('../../enums'); -const Client = require('../../models/client'); - -class WebSocketServer extends EventEmitter { - constructor({ server, realm, config }) { - super(); - this.setMaxListeners(0); - this.realm = realm; - this.config = config; - - let path = this.config.path; - path = path + (path[path.length - 1] !== '/' ? '/' : '') + 'peerjs'; - - this._wss = new WSS({ path, server }); - - this._wss.on('connection', (socket, req) => this._onSocketConnection(socket, req)); - this._wss.on('error', (error) => this._onSocketError(error)); - } - - _onSocketConnection(socket, req) { - const { query = {} } = url.parse(req.url, true); - - const { id, token, key } = query; - - if (!id || !token || !key) { - return this._sendErrorAndClose(socket, Errors.INVALID_WS_PARAMETERS); - } - - if (key !== this.config.key) { - return this._sendErrorAndClose(socket, Errors.INVALID_KEY); - } - - const client = this.realm.getClientById(id); - - if (client) { - if (token !== client.getToken()) { - // ID-taken, invalid token - socket.send(JSON.stringify({ - type: MessageType.ID_TAKEN, - payload: { msg: 'ID is taken' } - })); - - return socket.close(); - } - - return this._configureWS(socket, client); - } - - this._registerClient({ socket, id, token }); - } - - _onSocketError(error) { - // handle error - this.emit('error', error); - } - - _registerClient({ socket, id, token }) { - // Check concurrent limit - const clientsCount = this.realm.getClientsIds().length; - - if (clientsCount >= this.config.concurrent_limit) { - return this._sendErrorAndClose(socket, Errors.CONNECTION_LIMIT_EXCEED); - } - - const newClient = new Client({ id, token }); - this.realm.setClient(newClient, id); - socket.send(JSON.stringify({ type: MessageType.OPEN })); - - this._configureWS(socket, newClient); - } - - _configureWS(socket, client) { - client.setSocket(socket); - - // Cleanup after a socket closes. - socket.on('close', () => { - if (client.socket === socket) { - this.realm.removeClientById(client.getId()); - this.emit('close', client); - } - }); - - // Handle messages from peers. - socket.on('message', (data) => { - try { - const message = JSON.parse(data); - - message.src = client.getId(); - - this.emit('message', client, message); - } catch (e) { - this.emit('error', e); - } - }); - - this.emit('connection', client); - } - - _sendErrorAndClose(socket, msg) { - socket.send( - JSON.stringify({ - type: MessageType.ERROR, - payload: { msg } - }) - ); - - socket.close(); - } -} - -module.exports = WebSocketServer; diff --git a/src/services/webSocketServer/index.ts b/src/services/webSocketServer/index.ts new file mode 100644 index 0000000..3b237a1 --- /dev/null +++ b/src/services/webSocketServer/index.ts @@ -0,0 +1,143 @@ +import EventEmitter from "events"; +import { IncomingMessage } from "http"; +import url from "url"; +import WebSocketLib from "ws"; +import { IConfig } from "../../config"; +import { Errors, MessageType } from "../../enums"; +import { Client, IClient } from "../../models/client"; +import { IRealm } from "../../models/realm"; +import { MyWebSocket } from "./webSocket"; + +export interface IWebSocketServer extends EventEmitter { + readonly path: string; +} + +interface IAuthParams { + id?: string; + token?: string; + key?: string; +} + +type CustomConfig = Pick; + +const WS_PATH = 'peerjs'; + +export class WebSocketServer extends EventEmitter implements IWebSocketServer { + + public readonly path: string; + private readonly realm: IRealm; + private readonly config: CustomConfig; + public readonly socketServer: WebSocketLib.Server; + + constructor({ server, realm, config }: { server: any, realm: IRealm, config: CustomConfig; }) { + super(); + + this.setMaxListeners(0); + + this.realm = realm; + this.config = config; + + const path = this.config.path; + this.path = `${path}${path.endsWith('/') ? "" : "/"}${WS_PATH}`; + + this.socketServer = new WebSocketLib.Server({ path: this.path, server }); + + this.socketServer.on("connection", (socket: MyWebSocket, req) => this._onSocketConnection(socket, req)); + this.socketServer.on("error", (error: Error) => this._onSocketError(error)); + } + + private _onSocketConnection(socket: MyWebSocket, req: IncomingMessage): void { + const { query = {} } = url.parse(req.url!, true); + + const { id, token, key }: IAuthParams = query; + + if (!id || !token || !key) { + return this._sendErrorAndClose(socket, Errors.INVALID_WS_PARAMETERS); + } + + if (key !== this.config.key) { + return this._sendErrorAndClose(socket, Errors.INVALID_KEY); + } + + const client = this.realm.getClientById(id); + + if (client) { + if (token !== client.getToken()) { + // ID-taken, invalid token + socket.send(JSON.stringify({ + type: MessageType.ID_TAKEN, + payload: { msg: "ID is taken" } + })); + + return socket.close(); + } + + return this._configureWS(socket, client); + } + + this._registerClient({ socket, id, token }); + } + + private _onSocketError(error: Error): void { + // handle error + this.emit("error", error); + } + + private _registerClient({ socket, id, token }: + { + socket: MyWebSocket; + id: string; + token: string; + }): void { + // Check concurrent limit + const clientsCount = this.realm.getClientsIds().length; + + if (clientsCount >= this.config.concurrent_limit) { + return this._sendErrorAndClose(socket, Errors.CONNECTION_LIMIT_EXCEED); + } + + const newClient: IClient = new Client({ id, token }); + this.realm.setClient(newClient, id); + socket.send(JSON.stringify({ type: MessageType.OPEN })); + + this._configureWS(socket, newClient); + } + + private _configureWS(socket: MyWebSocket, client: IClient): void { + client.setSocket(socket); + + // Cleanup after a socket closes. + socket.on("close", () => { + if (client.getSocket() === socket) { + this.realm.removeClientById(client.getId()); + this.emit("close", client); + } + }); + + // Handle messages from peers. + socket.on("message", (data: WebSocketLib.Data) => { + try { + const message = JSON.parse(data as string); + + message.src = client.getId(); + + this.emit("message", client, message); + } catch (e) { + this.emit("error", e); + } + }); + + this.emit("connection", client); + } + + private _sendErrorAndClose(socket: MyWebSocket, msg: Errors): void { + socket.send( + JSON.stringify({ + type: MessageType.ERROR, + payload: { msg } + }) + ); + + socket.close(); + } +} diff --git a/src/services/webSocketServer/webSocket.ts b/src/services/webSocketServer/webSocket.ts new file mode 100644 index 0000000..1e04565 --- /dev/null +++ b/src/services/webSocketServer/webSocket.ts @@ -0,0 +1,4 @@ +import EventEmitter from "events"; +import WebSocketLib from "ws"; + +export type MyWebSocket = WebSocketLib & EventEmitter; diff --git a/test/messageHandler/handlers/heartbeat/index.js b/test/messageHandler/handlers/heartbeat/index.ts similarity index 58% rename from test/messageHandler/handlers/heartbeat/index.js rename to test/messageHandler/handlers/heartbeat/index.ts index de76b79..c13d7c0 100644 --- a/test/messageHandler/handlers/heartbeat/index.js +++ b/test/messageHandler/handlers/heartbeat/index.ts @@ -1,6 +1,6 @@ -const { expect } = require('chai'); -const Client = require('../../../../src/models/client'); -const heartbeatHandler = require('../../../../src/messageHandler/handlers/heartbeat'); +import { expect } from 'chai'; +import { Client } from '../../../../src/models/client'; +import { HeartbeatHandler } from '../../../../src/messageHandler/handlers'; describe('Heartbeat handler', () => { it('should update last ping time', () => { @@ -9,7 +9,7 @@ describe('Heartbeat handler', () => { const nowTime = new Date().getTime(); - heartbeatHandler(client); + HeartbeatHandler(client); expect(client.getLastPing()).to.be.closeTo(nowTime, 2); }); diff --git a/test/messageHandler/handlers/transmission/index.ts b/test/messageHandler/handlers/transmission/index.ts new file mode 100644 index 0000000..83b1ff6 --- /dev/null +++ b/test/messageHandler/handlers/transmission/index.ts @@ -0,0 +1,96 @@ +import { expect } from 'chai'; +import { Client } from '../../../../src/models/client'; +import { TransmissionHandler } from '../../../../src/messageHandler/handlers'; +import { Realm } from '../../../../src/models/realm'; +import { MessageType } from '../../../../src/enums'; +import { MyWebSocket } from '../../../../src/services/webSocketServer/webSocket'; + +const createFakeSocket = (): MyWebSocket => { + /* eslint-disable @typescript-eslint/no-empty-function */ + const sock = { + send: (): void => { }, + close: (): void => { }, + on: (): void => { }, + }; + /* eslint-enable @typescript-eslint/no-empty-function */ + + return (sock as unknown as MyWebSocket); +}; + +describe('Transmission handler', () => { + it('should save message in queue when destination client not connected', () => { + const realm = new Realm(); + const handleTransmission = TransmissionHandler({ realm }); + + const clientFrom = new Client({ id: 'id1', token: '' }); + const idTo = 'id2'; + realm.setClient(clientFrom, clientFrom.getId()); + + handleTransmission(clientFrom, { type: MessageType.OFFER, src: clientFrom.getId(), dst: idTo }); + + expect(realm.getMessageQueueById(idTo)?.getMessages().length).to.be.eq(1); + }); + + it('should not save LEAVE and EXPIRE messages in queue when destination client not connected', () => { + const realm = new Realm(); + const handleTransmission = TransmissionHandler({ realm }); + + const clientFrom = new Client({ id: 'id1', token: '' }); + const idTo = 'id2'; + realm.setClient(clientFrom, clientFrom.getId()); + + handleTransmission(clientFrom, { type: MessageType.LEAVE, src: clientFrom.getId(), dst: idTo }); + handleTransmission(clientFrom, { type: MessageType.EXPIRE, src: clientFrom.getId(), dst: idTo }); + + expect(realm.getMessageQueueById(idTo)).to.be.undefined; + }); + + it('should send message to destination client when destination client connected', () => { + const realm = new Realm(); + const handleTransmission = TransmissionHandler({ realm }); + + const clientFrom = new Client({ id: 'id1', token: '' }); + const clientTo = new Client({ id: 'id2', token: '' }); + const socketTo = createFakeSocket(); + clientTo.setSocket(socketTo); + realm.setClient(clientTo, clientTo.getId()); + + let sent = false; + socketTo.send = (): void => { + sent = true; + }; + + handleTransmission(clientFrom, { type: MessageType.OFFER, src: clientFrom.getId(), dst: clientTo.getId() }); + + expect(sent).to.be.true; + }); + + it('should send LEAVE message to source client when sending to destination client failed', () => { + const realm = new Realm(); + const handleTransmission = TransmissionHandler({ realm }); + + const clientFrom = new Client({ id: 'id1', token: '' }); + const clientTo = new Client({ id: 'id2', token: '' }); + const socketFrom = createFakeSocket(); + const socketTo = createFakeSocket(); + clientFrom.setSocket(socketFrom); + clientTo.setSocket(socketTo); + realm.setClient(clientFrom, clientFrom.getId()); + realm.setClient(clientTo, clientTo.getId()); + + let sent = false; + socketFrom.send = (data: string): void => { + if (JSON.parse(data)?.type === MessageType.LEAVE) { + sent = true; + } + }; + + socketTo.send = (): void => { + throw Error(); + }; + + handleTransmission(clientFrom, { type: MessageType.OFFER, src: clientFrom.getId(), dst: clientTo.getId() }); + + expect(sent).to.be.true; + }); +}); diff --git a/test/messageHandler/handlersRegistry.ts b/test/messageHandler/handlersRegistry.ts new file mode 100644 index 0000000..6791056 --- /dev/null +++ b/test/messageHandler/handlersRegistry.ts @@ -0,0 +1,23 @@ +import { expect } from 'chai'; +import { HandlersRegistry } from '../../src/messageHandler/handlersRegistry'; +import { Handler } from '../../src/messageHandler/handler'; +import { MessageType } from '../../src/enums'; + +describe('HandlersRegistry', () => { + it('should execute handler for message type', () => { + const handlersRegistry = new HandlersRegistry(); + + let handled = false; + + const handler: Handler = (): boolean => { + handled = true; + return true; + }; + + handlersRegistry.registerHandler(MessageType.OPEN, handler); + + handlersRegistry.handle(undefined, { type: MessageType.OPEN, src: 'src', dst: 'dst' }); + + expect(handled).to.be.true; + }); +}); diff --git a/test/models/messageQueue.ts b/test/models/messageQueue.ts new file mode 100644 index 0000000..41118f2 --- /dev/null +++ b/test/models/messageQueue.ts @@ -0,0 +1,62 @@ +import { expect } from 'chai'; +import { MessageQueue } from '../../src/models/messageQueue'; +import { MessageType } from '../../src/enums'; +import { IMessage } from '../../src/models/message'; +import { wait } from '../utils'; + +describe('MessageQueue', () => { + const createTestMessage = (): IMessage => { + return { + type: MessageType.OPEN, + src: 'src', + dst: 'dst' + }; + }; + + describe('#addMessage', () => { + it('should add message to queue', () => { + const queue = new MessageQueue(); + queue.addMessage(createTestMessage()); + expect(queue.getMessages().length).to.eq(1); + }); + }); + + describe('#readMessage', () => { + it('should return undefined for empty queue', () => { + const queue = new MessageQueue(); + expect(queue.readMessage()).to.be.undefined; + }); + + it('should return message if any exists in queue', () => { + const queue = new MessageQueue(); + const message = createTestMessage(); + queue.addMessage(message); + + expect(queue.readMessage()).to.deep.eq(message); + expect(queue.readMessage()).to.be.undefined; + }); + }); + + describe('#getLastReadAt', () => { + it('should not be changed if no messages when read', () => { + const queue = new MessageQueue(); + const lastReadAt = queue.getLastReadAt(); + queue.readMessage(); + expect(queue.getLastReadAt()).to.be.eq(lastReadAt); + }); + + it('should be changed when read message', async () => { + const queue = new MessageQueue(); + const lastReadAt = queue.getLastReadAt(); + queue.addMessage(createTestMessage()); + + await wait(10); + + expect(queue.getLastReadAt()).to.be.eq(lastReadAt); + + queue.readMessage(); + + expect(queue.getLastReadAt()).to.be.greaterThan(lastReadAt + 10); + }); + }); +}); diff --git a/test/models/realm.js b/test/models/realm.ts similarity index 83% rename from test/models/realm.js rename to test/models/realm.ts index 2c8ee39..6daf9c2 100644 --- a/test/models/realm.js +++ b/test/models/realm.ts @@ -1,12 +1,12 @@ -const { expect } = require('chai'); -const Realm = require('../../src/models/realm'); -const Client = require('../../src/models/client'); +import { expect } from 'chai'; +import { Realm } from '../../src/models/realm'; +import { Client } from '../../src/models/client'; describe('Realm', () => { describe('#generateClientId', () => { - it('should generate a 16-character ID', () => { + it('should generate a 36-character UUID', () => { const realm = new Realm(); - expect(realm.generateClientId().length).to.eq(16); + expect(realm.generateClientId().length).to.eq(36); expect(realm.generateClientId(() => 'abcd')).to.eq('abcd'); }); }); diff --git a/test/services/checkBrokenConnections/index.js b/test/services/checkBrokenConnections/index.js deleted file mode 100644 index 1d77ad5..0000000 --- a/test/services/checkBrokenConnections/index.js +++ /dev/null @@ -1,43 +0,0 @@ -const { expect } = require('chai'); -const Client = require('../../../src/models/client'); -const Realm = require('../../../src/models/realm'); -const checkBrokenConnectionsBuilder = require('../../../src/services/checkBrokenConnections'); - -describe('checkBrokenConnections service', () => { - it('should remove client after 2 checks', (done) => { - const realm = new Realm(); - const doubleCheckTime = 55;//~ equals to checkBrokenConnections.CHECK_INTERVAL * 2 - const checkBrokenConnections = checkBrokenConnectionsBuilder({ realm, config: { alive_timeout: doubleCheckTime }, checkInterval: 30 }); - const client = new Client({ id: 'id', token: '' }); - realm.setClient(client, 'id'); - - checkBrokenConnections.start(); - - setTimeout(() => { - expect(realm.getClientById('id')).to.be.undefined; - checkBrokenConnections.stop(); - done(); - }, checkBrokenConnections.CHECK_INTERVAL * 2 + 3); - }); - - it('should remove client after 1 ping', (done) => { - const realm = new Realm(); - const doubleCheckTime = 55;//~ equals to checkBrokenConnections.CHECK_INTERVAL * 2 - const checkBrokenConnections = checkBrokenConnectionsBuilder({ realm, config: { alive_timeout: doubleCheckTime }, checkInterval: 30 }); - const client = new Client({ id: 'id', token: '' }); - realm.setClient(client, 'id'); - - checkBrokenConnections.start(); - - //set ping after first check - setTimeout(() => { - client.setLastPing(new Date().getTime()); - - setTimeout(() => { - expect(realm.getClientById('id')).to.be.undefined; - checkBrokenConnections.stop(); - done(); - }, checkBrokenConnections.CHECK_INTERVAL * 2 + 10); - }, checkBrokenConnections.CHECK_INTERVAL); - }); -}); diff --git a/test/services/checkBrokenConnections/index.ts b/test/services/checkBrokenConnections/index.ts new file mode 100644 index 0000000..437f054 --- /dev/null +++ b/test/services/checkBrokenConnections/index.ts @@ -0,0 +1,44 @@ +import { expect } from 'chai'; +import { Client } from '../../../src/models/client'; +import { Realm } from '../../../src/models/realm'; +import { CheckBrokenConnections } from '../../../src/services/checkBrokenConnections'; +import { wait } from '../../utils'; + +describe('CheckBrokenConnections', () => { + it('should remove client after 2 checks', async () => { + const realm = new Realm(); + const doubleCheckTime = 55;//~ equals to checkBrokenConnections.checkInterval * 2 + const checkBrokenConnections = new CheckBrokenConnections({ realm, config: { alive_timeout: doubleCheckTime }, checkInterval: 30 }); + const client = new Client({ id: 'id', token: '' }); + realm.setClient(client, 'id'); + + checkBrokenConnections.start(); + + await wait(checkBrokenConnections.checkInterval * 2 + 30); + + expect(realm.getClientById('id')).to.be.undefined; + + checkBrokenConnections.stop(); + }); + + it('should remove client after 1 ping', async () => { + const realm = new Realm(); + const doubleCheckTime = 55;//~ equals to checkBrokenConnections.checkInterval * 2 + const checkBrokenConnections = new CheckBrokenConnections({ realm, config: { alive_timeout: doubleCheckTime }, checkInterval: 30 }); + const client = new Client({ id: 'id', token: '' }); + realm.setClient(client, 'id'); + + checkBrokenConnections.start(); + + //set ping after first check + await wait(checkBrokenConnections.checkInterval); + + client.setLastPing(new Date().getTime()); + + await wait(checkBrokenConnections.checkInterval * 2 + 10); + + expect(realm.getClientById('id')).to.be.undefined; + + checkBrokenConnections.stop(); + }); +}); diff --git a/test/services/messagesExpire/index.ts b/test/services/messagesExpire/index.ts new file mode 100644 index 0000000..eac362b --- /dev/null +++ b/test/services/messagesExpire/index.ts @@ -0,0 +1,78 @@ +import { expect } from 'chai'; +import { Client } from '../../../src/models/client'; +import { Realm } from '../../../src/models/realm'; +import { IMessage } from '../../../src/models/message'; +import { MessagesExpire } from '../../../src/services/messagesExpire'; +import { MessageHandler } from '../../../src/messageHandler'; +import { MessageType } from '../../../src/enums'; +import { wait } from '../../utils'; + +describe('MessagesExpire', () => { + const createTestMessage = (): IMessage => { + return { + type: MessageType.OPEN, + src: 'src', + dst: 'dst' + }; + }; + + it('should remove client if no read from queue', async () => { + const realm = new Realm(); + const messageHandler = new MessageHandler(realm); + const checkInterval = 10; + const expireTimeout = 50; + const config = { cleanup_out_msgs: checkInterval, expire_timeout: expireTimeout }; + + const messagesExpire = new MessagesExpire({ realm, config, messageHandler }); + + const client = new Client({ id: 'id', token: '' }); + realm.setClient(client, 'id'); + realm.addMessageToQueue(client.getId(), createTestMessage()); + + messagesExpire.startMessagesExpiration(); + + await wait(checkInterval * 2); + + expect(realm.getMessageQueueById(client.getId())?.getMessages().length).to.be.eq(1); + + await wait(expireTimeout); + + expect(realm.getMessageQueueById(client.getId())).to.be.undefined; + + messagesExpire.stopMessagesExpiration(); + }); + + it('should fire EXPIRE message', async () => { + const realm = new Realm(); + const messageHandler = new MessageHandler(realm); + const checkInterval = 10; + const expireTimeout = 50; + const config = { cleanup_out_msgs: checkInterval, expire_timeout: expireTimeout }; + + const messagesExpire = new MessagesExpire({ realm, config, messageHandler }); + + const client = new Client({ id: 'id', token: '' }); + realm.setClient(client, 'id'); + realm.addMessageToQueue(client.getId(), createTestMessage()); + + let handled = false; + + messageHandler.handle = (client, message): boolean => { + expect(client).to.be.undefined; + expect(message.type).to.be.eq(MessageType.EXPIRE); + + handled = true; + + return true; + }; + + messagesExpire.startMessagesExpiration(); + + await wait(checkInterval * 2); + await wait(expireTimeout); + + expect(handled).to.be.true; + + messagesExpire.stopMessagesExpiration(); + }); +}); diff --git a/test/services/webSocketServer/index.ts b/test/services/webSocketServer/index.ts new file mode 100644 index 0000000..705b2a6 --- /dev/null +++ b/test/services/webSocketServer/index.ts @@ -0,0 +1,195 @@ +import { expect } from 'chai'; +import { Server, WebSocket } from 'mock-socket'; +import { Realm } from '../../../src/models/realm'; +import { WebSocketServer } from '../../../src/services/webSocketServer'; +import { Errors, MessageType } from '../../../src/enums'; +import { wait } from '../../utils'; + +type Destroyable = T & { destroy?: () => Promise; }; + +const checkOpen = async (c: WebSocket): Promise => { + return new Promise(resolve => { + c.onmessage = (event: object & { data?: string; }): void => { + c.onmessage = null; + const message = JSON.parse(event.data as string); + resolve(message.type === MessageType.OPEN); + }; + }); +}; + +const checkSequence = async (c: WebSocket, msgs: { type: MessageType; error?: Errors; }[]): Promise => { + return new Promise(resolve => { + const restMessages = [...msgs]; + + const finish = (success = false): void => { + c.onmessage = null; + resolve(success); + }; + + c.onmessage = (event: object & { data?: string; }): void => { + const [mes] = restMessages; + + if (!mes) { + return finish(); + } + + restMessages.shift(); + + const message = JSON.parse(event.data as string); + if (message.type !== mes.type) { + return finish(); + } + + const isOk = !mes.error || message.payload?.msg === mes.error; + + if (!isOk) { + return finish(); + } + + if (restMessages.length === 0) { + finish(true); + } + }; + }); +}; + +const createTestServer = ({ realm, config, url }: { realm: Realm; config: { path: string; key: string; concurrent_limit: number; }; url: string; }): Destroyable => { + const server = new Server(url); + const webSocketServer: Destroyable = new WebSocketServer({ server, realm, config }); + + server.on('connection', (socket: WebSocket & { on?: (eventName: string, callback: () => void) => void; }) => { + const s = webSocketServer.socketServer; + s.emit('connection', socket, { url: socket.url }); + + socket.onclose = (): void => { + const userId = socket.url.split('?')[1]?.split('&').find(p => p.startsWith('id'))?.split('=')[1]; + + if (!userId) return; + + const client = realm.getClientById(userId); + + const clientSocket = client?.getSocket(); + + if (!clientSocket) return; + + (clientSocket as unknown as WebSocket).listeners['server::close']?.forEach((s: () => void) => s()); + }; + + socket.onmessage = (event: object & { data?: string; }): void => { + const userId = socket.url.split('?')[1]?.split('&').find(p => p.startsWith('id'))?.split('=')[1]; + + if (!userId) return; + + const client = realm.getClientById(userId); + + const clientSocket = client?.getSocket(); + + if (!clientSocket) return; + + (clientSocket as unknown as WebSocket).listeners['server::message']?.forEach((s: (data: object) => void) => s(event)); + }; + }); + + webSocketServer.destroy = async (): Promise => { + server.close(); + }; + + return webSocketServer; +}; + +describe('WebSocketServer', () => { + + it('should return valid path', () => { + const realm = new Realm(); + const config = { path: '/', key: 'testKey', concurrent_limit: 1 }; + const config2 = { ...config, path: 'path' }; + const server = new Server('path1'); + const server2 = new Server('path2'); + + const webSocketServer = new WebSocketServer({ server, realm, config }); + + expect(webSocketServer.path).to.be.eq('/peerjs'); + + const webSocketServer2 = new WebSocketServer({ server: server2, realm, config: config2 }); + + expect(webSocketServer2.path).to.be.eq('path/peerjs'); + + server.stop(); + server2.stop(); + }); + + it(`should check client's params`, async () => { + const realm = new Realm(); + const config = { path: '/', key: 'testKey', concurrent_limit: 1 }; + const fakeURL = 'ws://localhost:8080/peerjs'; + + const getError = async (url: string, validError: Errors = Errors.INVALID_WS_PARAMETERS): Promise => { + const webSocketServer = createTestServer({ url, realm, config }); + + const ws = new WebSocket(url); + + const errorSent = await checkSequence(ws, [{ type: MessageType.ERROR, error: validError }]); + + ws.close(); + + await webSocketServer.destroy?.(); + + return errorSent; + }; + + expect(await getError(fakeURL)).to.be.true; + expect(await getError(`${fakeURL}?key=${config.key}`)).to.be.true; + expect(await getError(`${fakeURL}?key=${config.key}&id=1`)).to.be.true; + expect(await getError(`${fakeURL}?key=notValidKey&id=userId&token=userToken`, Errors.INVALID_KEY)).to.be.true; + }); + + it(`should check concurrent limit`, async () => { + const realm = new Realm(); + const config = { path: '/', key: 'testKey', concurrent_limit: 1 }; + const fakeURL = 'ws://localhost:8080/peerjs'; + + const createClient = (id: string): Destroyable => { + const url = `${fakeURL}?key=${config.key}&id=${id}&token=${id}`; + const webSocketServer = createTestServer({ url, realm, config }); + const ws: Destroyable = new WebSocket(url); + + ws.destroy = async (): Promise => { + ws.close(); + + wait(10); + + webSocketServer.destroy?.(); + + wait(10); + + ws.destroy = undefined; + }; + + return ws; + }; + + + const c1 = createClient('1'); + + expect(await checkOpen(c1)).to.be.true; + + const c2 = createClient('2'); + + expect(await checkSequence(c2, [ + { type: MessageType.ERROR, error: Errors.CONNECTION_LIMIT_EXCEED } + ])).to.be.true; + + await c1.destroy?.(); + await c2.destroy?.(); + + await wait(10); + + expect(realm.getClientsIds().length).to.be.eq(0); + + const c3 = createClient('3'); + + expect(await checkOpen(c3)).to.be.true; + + await c3.destroy?.(); + }); +}); diff --git a/test/utils.ts b/test/utils.ts new file mode 100644 index 0000000..cbbd648 --- /dev/null +++ b/test/utils.ts @@ -0,0 +1 @@ +export const wait = (ms: number): Promise => new Promise(resolve => setTimeout(resolve, ms)); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3c7af7a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "lib": [ + "esnext" + ], + "target": "es2016", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "downlevelIteration": true, + "moduleResolution": "node", + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": false, + "outDir": "dist" + }, + "include": [ + "./src/**/*", + ], + "exclude": [ + "test", + "bin", + ] +} \ No newline at end of file