diff --git a/examples/websocket/client.js b/examples/websocket/client.js new file mode 100644 index 0000000..3cb0600 --- /dev/null +++ b/examples/websocket/client.js @@ -0,0 +1,13 @@ +const WebSocket = require('isomorphic-ws'); +const jayson = require('../../'); + +const client = jayson.client.websocket({ + url: 'ws://localhost:12345', +}); + +client.ws.on('open', function () { + client.request('add', [1,2,3,4], function (err, result) { + console.log(err, result); + client.ws.close(); + }); +}); diff --git a/examples/websocket/server.js b/examples/websocket/server.js new file mode 100644 index 0000000..98a090b --- /dev/null +++ b/examples/websocket/server.js @@ -0,0 +1,13 @@ +const WebSocket = require('ws'); +const jayson = require('../../'); + +const server = new jayson.Server({ + add: function (args, done) { + const sum = args.reduce((sum, val) => sum + val, 0); + done(null, sum); + }, +}); + +const wss = server.websocket({ + port: 12345, +}); diff --git a/index.d.ts b/index.d.ts index 00d592e..897980a 100644 --- a/index.d.ts +++ b/index.d.ts @@ -4,6 +4,7 @@ import https = require('https'); import http = require('http'); import events = require('events'); import Stream = require('stream'); +import WebSocket = require('isomorphic-ws'); import * as connect from 'connect'; export interface UtilsJSONParseOptions { @@ -221,6 +222,7 @@ export declare class Server extends events.EventEmitter { https(options?: HttpsServerOptions): HttpsServer; tcp(options?: TcpServerOptions): TcpServer; tls(options?: TlsServerOptions): TlsServer; + websocket(options?: WebsocketServerOptions): WebsocketServer; middleware(options?: MiddlewareServerOptions): connect.HandleFunction; method(name: string, definition: MethodLike): void; @@ -265,6 +267,14 @@ declare class TlsServer extends tls.Server { constructor(server: Server, options?: TlsServerOptions); } +export interface WebsocketServerOptions extends ServerOptions, WebSocket.ServerOptions { + wss?: WebSocket.Server; +} + +declare class WebsocketServer { + constructor(server: Server, options?: WebsocketServerOptions); +} + type JSONParseReviver = (key: string, value: any) => any; type JSONStringifyReplacer = (key: string, value: any) => any; @@ -306,6 +316,16 @@ declare class HttpsClient extends Client { constructor(options?: HttpsClientOptions); } +export interface WebsocketClientOptions extends ClientOptions { + url?: string; + ws?: WebSocket; + timeout?: number; +} + +declare class WebsocketClient extends Client { + constructor(options?: WebsocketClientOptions); +} + export declare class Client extends events.EventEmitter { constructor(server: Server, options?: ClientOptions); constructor(options: ClientOptions); @@ -314,6 +334,7 @@ export declare class Client extends events.EventEmitter { static https(options?: HttpsClientOptions): HttpsClient; static tcp(options?: TcpClientOptions): TcpClient; static tls(options?: TlsClientOptions): TlsClient; + static websocket(options?: WebsocketClientOptions): WebsocketClient; request(method: string, params: RequestParamsLike, id?: string | null, callback?: JSONRPCCallbackType): JSONRPCRequest; request(method: string, params: RequestParamsLike, callback?: JSONRPCCallbackType): JSONRPCRequest; diff --git a/lib/client/index.js b/lib/client/index.js index 4440564..a313bea 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -80,6 +80,13 @@ Client.tls = require('./tls'); */ Client.browser = require('./browser'); +/** + * Websocket client constructor + * @type ClientWebsocket + * @static + */ +Client.websocket = require('./websocket'); + /** * Creates a request and dispatches it if given a callback. * @param {String|Array} method A batch request if passed an Array, or a method name if passed a String @@ -173,7 +180,7 @@ Client.prototype._request = function(request, callback) { }; /** - * Parses a response from a server + * Parses a response from a server, taking care of sugaring * @param {Object} err Error to pass on that is unrelated to the actual response * @param {Object} response JSON-RPC 1.0 or 2.0 response * @param {Function} callback Callback that will receive different arguments depending on the amount of parameters diff --git a/lib/client/websocket.js b/lib/client/websocket.js new file mode 100644 index 0000000..427c117 --- /dev/null +++ b/lib/client/websocket.js @@ -0,0 +1,133 @@ +'use strict'; + +const WebSocket = require('isomorphic-ws'); +const utils = require('../utils'); +const delay = require('delay'); +const Client = require('../client'); + +/** + * Constructor for a Jayson Websocket Client + * @class ClientWebsocket + * @constructor + * @extends Client + * @param {Object} [options] + * @param {String} [options.url] When options.ws not provided this will be the URL to open the websocket to + * @param {ws.WebSocket} [options.ws] When not provided will create a WebSocket instance with options.url + * @param {Number} [options.timeout] Will wait this long in ms until callbacking with an error + * @return {ClientWebsocket} + */ +const ClientWebsocket = function(options) { + if(!(this instanceof ClientWebsocket)) { + return new ClientWebsocket(options); + } + Client.call(this, options); + + const defaults = utils.merge(this.options, {}); + this.options = utils.merge(defaults, options || {}); + + const self = this; + + this.ws = this.options.ws || new WebSocket(this.options.url); + this.outstandingRequests = []; + this.handlers = {}; + + this.handlers.message = function (str) { + utils.JSON.parse(str, self.options, function(err, response) { + if (err) { + // invalid JSON is ignored + return; + } + + if (Array.isArray(response)) { + + // we have a batch reply + const matchingRequest = self.outstandingRequests.find(function ([request]) { + if (Array.isArray(request)) { + // a batch is considered matching if at least one response id matches one request id + return response.some(function (resp) { + if (utils.Response.isValidResponse(resp)) { + return request.some(function (req) { + return req.id === resp.id; + }); + } + return false; + }); + } + }); + + if (matchingRequest) { + const [ request, resolve ] = matchingRequest; + return resolve(response); + } + + } else if (utils.Response.isValidResponse(response)) { + + const matchingRequest = self.outstandingRequests.find(function ([request]) { + return !Array.isArray(request) && request.id === response.id; + }); + + if (matchingRequest) { + const [ request, resolve ] = matchingRequest; + return resolve(response); + } + } + + }); + }; + + this.ws.on('message', this.handlers.message); +}; +require('util').inherits(ClientWebsocket, Client); + +module.exports = ClientWebsocket; + +/** + * @desc Removes all event listeners from Websocket instance which cancels all outstanding requests too + */ +ClientWebsocket.prototype.unlisten = function () { + for (const eventName in this.handlers) { + this.ws.off(eventName, this.handlers[eventName]); + } +}; + +ClientWebsocket.prototype._request = function(request, callback) { + const self = this; + const { ws, options } = this; + + // we have to remove the object representing this request when the promise resolves/rejects + let outstandingItem; + + Promise.race([ + options.timeout > 0 ? delay(options.timeout).then(function () { + throw new Error('timeout reached after ' + options.timeout + ' ms'); + }) : null, + new Promise(function (resolve, reject) { + utils.JSON.stringify(request, options, function(err, body) { + if (err) { + return resolve(err); + } + + ws.send(body); + + if (utils.Request.isNotification(request)) { + // notifications callback immediately since they don't have a reply + return resolve(); + } + + outstandingItem = [request, resolve, reject]; + self.outstandingRequests.push(outstandingItem); + }); + }), + ].filter(v => v !== null)).then(function (result) { + removeOutstandingRequest(); + callback(null, result); + }).catch(function (err) { + removeOutstandingRequest(); + callback(err); + }); + + function removeOutstandingRequest () { + if (!outstandingItem) return; + self.outstandingRequests = self.outstandingRequests.filter(v => v !== outstandingItem); + } +}; diff --git a/lib/server/http.js b/lib/server/http.js index 74fa922..f498169 100644 --- a/lib/server/http.js +++ b/lib/server/http.js @@ -11,9 +11,9 @@ const utils = require('../utils'); * @param {Object} [options] Options for this instance * @return {ServerHttp} */ -const HttpServer = function(server, options) { - if(!(this instanceof HttpServer)) { - return new HttpServer(server, options); +const ServerHttp = function(server, options) { + if(!(this instanceof ServerHttp)) { + return new ServerHttp(server, options); } this.options = utils.merge(server.options, options || {}); @@ -21,6 +21,6 @@ const HttpServer = function(server, options) { const listener = utils.getHttpListener(this, server); http.Server.call(this, listener); }; -require('util').inherits(HttpServer, http.Server); +require('util').inherits(ServerHttp, http.Server); -module.exports = HttpServer; +module.exports = ServerHttp; diff --git a/lib/server/https.js b/lib/server/https.js index 7a36490..2f54c6c 100644 --- a/lib/server/https.js +++ b/lib/server/https.js @@ -11,9 +11,9 @@ const utils = require('../utils'); * @param {Object} [options] Options for this instance * @return {ServerHttps} */ -const HttpsServer = function(server, options) { - if(!(this instanceof HttpsServer)) { - return new HttpsServer(server, options); +const ServerHttps = function(server, options) { + if(!(this instanceof ServerHttps)) { + return new ServerHttps(server, options); } this.options = utils.merge(server.options, options || {}); @@ -21,6 +21,6 @@ const HttpsServer = function(server, options) { const listener = utils.getHttpListener(this, server); https.Server.call(this, this.options, listener); }; -require('util').inherits(HttpsServer, https.Server); +require('util').inherits(ServerHttps, https.Server); -module.exports = HttpsServer; +module.exports = ServerHttps; diff --git a/lib/server/index.js b/lib/server/index.js index 094b625..3fd4beb 100644 --- a/lib/server/index.js +++ b/lib/server/index.js @@ -83,6 +83,7 @@ Server.interfaces = { https: require('./https'), tcp: require('./tcp'), tls: require('./tls'), + websocket: require('./websocket'), middleware: require('./middleware') }; diff --git a/lib/server/tcp.js b/lib/server/tcp.js index 0bd9d4d..ae6ae9e 100644 --- a/lib/server/tcp.js +++ b/lib/server/tcp.js @@ -11,18 +11,18 @@ const utils = require('../utils'); * @param {Object} [options] Options for this instance * @return {ServerTcp} */ -const TcpServer = function(server, options) { - if(!(this instanceof TcpServer)) { - return new TcpServer(server, options); +const ServerTcp = function(server, options) { + if(!(this instanceof ServerTcp)) { + return new ServerTcp(server, options); } this.options = utils.merge(server.options, options || {}); net.Server.call(this, getTcpListener(this, server)); }; -require('util').inherits(TcpServer, net.Server); +require('util').inherits(ServerTcp, net.Server); -module.exports = TcpServer; +module.exports = ServerTcp; /** * Returns a TCP connection listener bound to the server in the argument. @@ -58,8 +58,7 @@ function getTcpListener(self, server) { // ends the request with an error code function respondError(err) { - const Server = require('../server'); - const error = server.error(Server.errors.PARSE_ERROR, null, String(err)); + const error = server.error(-32700, null, String(err)); const response = utils.response(error, undefined, undefined, self.options.version); utils.JSON.stringify(response, options, function(err, body) { if(err) { diff --git a/lib/server/tls.js b/lib/server/tls.js index 7d63d6f..88acd7f 100644 --- a/lib/server/tls.js +++ b/lib/server/tls.js @@ -11,18 +11,18 @@ const utils = require('../utils'); * @param {Object} [options] Options for this instance * @return {ServerTls} */ -const TlsServer = function(server, options) { - if(!(this instanceof TlsServer)) { - return new TlsServer(server, options); +const ServerTls = function(server, options) { + if(!(this instanceof ServerTls)) { + return new ServerTls(server, options); } this.options = utils.merge(server.options, options || {}); tls.Server.call(this, this.options, getTlsListener(this, server)); }; -require('util').inherits(TlsServer, tls.Server); +require('util').inherits(ServerTls, tls.Server); -module.exports = TlsServer; +module.exports = ServerTls; /** * Returns a TLS-encrypted TCP connection listener bound to the server in the argument. @@ -58,8 +58,7 @@ function getTlsListener(self, server) { // ends the request with an error code function respondError(err) { - const Server = require('../server'); - const error = server.error(Server.errors.PARSE_ERROR, null, String(err)); + const error = server.error(-32700, null, String(err)); const response = utils.response(error, undefined, undefined, self.options.version); utils.JSON.stringify(response, options, function(err, body) { if(err) { diff --git a/lib/server/websocket.js b/lib/server/websocket.js new file mode 100644 index 0000000..954fecb --- /dev/null +++ b/lib/server/websocket.js @@ -0,0 +1,62 @@ +'use strict'; + +const WebSocket = require('isomorphic-ws'); +const utils = require('../utils'); + +/** + * Constructor for a Jayson Websocket Server + * @name ServerWebsocket + * @param {Server} server Server instance + * @param {Object} [options] Options for this instance + * @param {ws.Websocket.Server} [options.wss] When provided will not create a new ws.WebSocket.Server but use this one + * @return {ws.WebSocket.Server} + */ +const ServerWebsocket = function(server, options) { + const jaysonOptions = utils.merge(server.options, options || {}); + const wss = options.wss || new WebSocket.Server(options); + + wss.on('connection', onConnection); + + function onConnection (ws) { + // every message received on the socket is handled as a JSON-RPC message + ws.on('message', function (buf) { + const str = Buffer.isBuffer(buf) ? buf.toString('utf8') : buf; + utils.JSON.parse(str, jaysonOptions, function(err, request) { + if (err) { + return respondError(err); + } + + server.call(request, function(error, success) { + const response = error || success; + if (response) { + utils.JSON.stringify(response, jaysonOptions, function (err, str) { + if (err) { + return respondError(err); + } + ws.send(str); + }); + } else { + // no response received at all, must be a notification which we do nothing about + } + }); + }); + }); + + // writes an error message to the client + function respondError (err) { + const error = server.error(-32700, null, String(err)); + const response = utils.response(error, undefined, undefined, jaysonOptions.version); + utils.JSON.stringify(response, jaysonOptions, function(err, str) { + if(err) { + // not much to do here, we couldn't even respond with an error + throw err; + } + ws.send(str); + }); + } + } + + return wss; +}; + +module.exports = ServerWebsocket; diff --git a/package-lock.json b/package-lock.json index 015cd71..9146f76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,25 +1,29 @@ { "name": "jayson", - "version": "3.6.2", + "version": "3.6.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "jayson", - "version": "3.6.2", + "version": "3.6.3", "license": "MIT", "dependencies": { "@types/connect": "^3.4.33", "@types/express-serve-static-core": "^4.17.9", "@types/lodash": "^4.14.159", "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", "commander": "^2.20.3", + "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "JSONStream": "^1.3.5", "lodash": "^4.17.20", - "uuid": "^3.4.0" + "uuid": "^3.4.0", + "ws": "^7.4.5" }, "bin": { "jayson": "bin/jayson.js" @@ -250,6 +254,14 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, + "node_modules/@types/ws": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -315,9 +327,9 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { "color-convert": "^1.9.0" @@ -372,15 +384,6 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -554,47 +557,38 @@ "dev": true }, "node_modules/catharsis": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", - "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "dependencies": { - "lodash": "^4.17.14" + "lodash": "^4.17.15" }, "engines": { - "node": ">= 8" + "node": ">= 10" } }, "node_modules/chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { - "ansi-styles": "^3.1.0", + "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" + "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, - "node_modules/chalk/node_modules/has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/chalk/node_modules/supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "has-flag": "^2.0.0" + "has-flag": "^3.0.0" }, "engines": { "node": ">=4" @@ -881,6 +875,17 @@ "node": ">= 0.4" } }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1491,9 +1496,9 @@ } }, "node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "node_modules/html-escaper": { @@ -1503,17 +1508,109 @@ "dev": true }, "node_modules/htmlparser2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", - "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", "dev": true, "dependencies": { - "domelementtype": "^1.3.0", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/htmlparser2/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domutils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", + "integrity": "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domutils/node_modules/domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/http-errors": { @@ -1744,6 +1841,14 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -1812,15 +1917,6 @@ "node": ">=6" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -1864,15 +1960,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/istanbul-lib-source-maps/node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/istanbul-reports": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", @@ -1920,25 +2007,25 @@ "dev": true }, "node_modules/jsdoc": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.5.tgz", - "integrity": "sha512-SbY+i9ONuxSK35cgVHaI8O9senTE4CDYAmGSDJ5l3+sfe62Ff4gy96osy6OW84t4K4A8iGnMrlRrsSItSNp3RQ==", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", + "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", "dev": true, "dependencies": { "@babel/parser": "^7.9.4", "bluebird": "^3.7.2", - "catharsis": "^0.8.11", + "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.1", "klaw": "^3.0.0", "markdown-it": "^10.0.0", "markdown-it-anchor": "^5.2.7", - "marked": "^0.8.2", + "marked": "^2.0.3", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.10.2" + "underscore": "~1.13.1" }, "bin": { "jsdoc": "jsdoc.js" @@ -1969,9 +2056,9 @@ } }, "node_modules/jsdoc/node_modules/marked": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", - "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.5.tgz", + "integrity": "sha512-yfCEUXmKhBPLOzEC7c+tc4XZdIeTdGoRCZakFMkCxodr7wDXqoapIME4wjcpBPJLNyUnKJ3e8rb8wlAgnLnaDw==", "dev": true, "bin": { "marked": "bin/marked" @@ -2208,36 +2295,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=", - "dev": true - }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "node_modules/lodash.isarray": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-4.0.0.tgz", - "integrity": "sha1-KspJayjEym1yZxUxNZDALm6jRAM=", - "dev": true - }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true - }, "node_modules/log-driver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", @@ -2259,44 +2322,6 @@ "node": ">=8" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/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, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -2390,15 +2415,6 @@ "source-map": "^0.6.1" } }, - "node_modules/merge-source-map/node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -2468,12 +2484,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mkdirp/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "node_modules/mocha": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", @@ -2616,15 +2626,6 @@ "node": ">=0.10.0" } }, - "node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/nyc": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", @@ -2664,15 +2665,6 @@ "node": ">=6" } }, - "node_modules/nyc/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -2822,6 +2814,12 @@ "node": ">=4" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=", + "dev": true + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2928,55 +2926,35 @@ } }, "node_modules/postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "dependencies": { - "chalk": "^2.3.0", + "chalk": "^2.4.2", "source-map": "^0.6.1", - "supports-color": "^4.4.0" + "supports-color": "^6.1.0" }, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/postcss/node_modules/has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss/node_modules/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, - "engines": { - "node": ">=0.10.0" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, "node_modules/postcss/node_modules/supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "dependencies": { - "has-flag": "^2.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, "node_modules/proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -3071,42 +3049,6 @@ "node": ">=6" } }, - "node_modules/readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "node_modules/readable-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/readable-stream/node_modules/string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/readdirp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", @@ -3256,19 +3198,15 @@ "dev": true }, "node_modules/sanitize-html": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.16.1.tgz", - "integrity": "sha512-w3++cRkD2krVl8Zn70l7OcrF+zQc6lF0EVzCrcyFA3LR3AofZb2AuC3HRWyyNq225kSvl5K7IxSpQMkTQ+bHkw==", + "version": "1.27.5", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz", + "integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==", "dev": true, "dependencies": { - "htmlparser2": "^3.9.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.isarray": "^4.0.0", - "lodash.mergewith": "^4.6.0", - "postcss": "^6.0.14", - "srcset": "^1.0.0", - "xtend": "^4.0.0" + "htmlparser2": "^4.1.0", + "lodash": "^4.17.15", + "parse-srcset": "^1.0.2", + "postcss": "^7.0.27" } }, "node_modules/semver": { @@ -3421,6 +3359,15 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/spawn-wrap": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", @@ -3473,19 +3420,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "node_modules/srcset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", - "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", - "dev": true, - "dependencies": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -3809,9 +3743,9 @@ "dev": true }, "node_modules/underscore": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", - "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", "dev": true }, "node_modules/unpipe": { @@ -3980,21 +3914,32 @@ "signal-exit": "^3.0.2" } }, + "node_modules/ws": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", + "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xmlcreate": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", "dev": true }, - "node_modules/xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true, - "engines": { - "node": ">=0.4" - } - }, "node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", @@ -4240,6 +4185,14 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, + "@types/ws": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ==", + "requires": { + "@types/node": "*" + } + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -4292,9 +4245,9 @@ "dev": true }, "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -4340,12 +4293,6 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -4496,38 +4443,32 @@ "dev": true }, "catharsis": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", - "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "requires": { - "lodash": "^4.17.14" + "lodash": "^4.17.15" } }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "^3.1.0", + "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" + "supports-color": "^5.3.0" }, "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "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": "^2.0.0" + "has-flag": "^3.0.0" } } } @@ -4771,6 +4712,11 @@ "object-keys": "^1.0.12" } }, + "delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -5267,9 +5213,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "html-escaper": { @@ -5279,17 +5225,82 @@ "dev": true }, "htmlparser2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", - "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", "dev": true, "requires": { - "domelementtype": "^1.3.0", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + }, + "dependencies": { + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "dependencies": { + "domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + } + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", + "integrity": "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "dependencies": { + "domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + } + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + } } }, "http-errors": { @@ -5473,6 +5484,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "requires": {} + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -5528,12 +5545,6 @@ "supports-color": "^6.1.0" }, "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -5572,12 +5583,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -5622,25 +5627,25 @@ "dev": true }, "jsdoc": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.5.tgz", - "integrity": "sha512-SbY+i9ONuxSK35cgVHaI8O9senTE4CDYAmGSDJ5l3+sfe62Ff4gy96osy6OW84t4K4A8iGnMrlRrsSItSNp3RQ==", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", + "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", "dev": true, "requires": { "@babel/parser": "^7.9.4", "bluebird": "^3.7.2", - "catharsis": "^0.8.11", + "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.1", "klaw": "^3.0.0", "markdown-it": "^10.0.0", "markdown-it-anchor": "^5.2.7", - "marked": "^0.8.2", + "marked": "^2.0.3", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.10.2" + "underscore": "~1.13.1" }, "dependencies": { "@babel/parser": { @@ -5656,9 +5661,9 @@ "dev": true }, "marked": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", - "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.5.tgz", + "integrity": "sha512-yfCEUXmKhBPLOzEC7c+tc4XZdIeTdGoRCZakFMkCxodr7wDXqoapIME4wjcpBPJLNyUnKJ3e8rb8wlAgnLnaDw==", "dev": true }, "mkdirp": { @@ -5850,36 +5855,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=", - "dev": true - }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "lodash.isarray": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-4.0.0.tgz", - "integrity": "sha1-KspJayjEym1yZxUxNZDALm6jRAM=", - "dev": true - }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true - }, "log-driver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", @@ -5893,37 +5874,6 @@ "dev": true, "requires": { "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "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" - } - } } }, "lru-cache": { @@ -6004,14 +5954,6 @@ "dev": true, "requires": { "source-map": "^0.6.1" - }, - "dependencies": { - "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 - } } }, "methods": { @@ -6063,14 +6005,6 @@ "dev": true, "requires": { "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } } }, "mocha": { @@ -6197,12 +6131,6 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, "nyc": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", @@ -6234,14 +6162,6 @@ "uuid": "^3.3.2", "yargs": "^13.2.2", "yargs-parser": "^13.0.0" - }, - "dependencies": { - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } } }, "oauth-sign": { @@ -6362,6 +6282,12 @@ "json-parse-better-errors": "^1.0.1" } }, + "parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=", + "dev": true + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -6443,45 +6369,27 @@ } }, "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { - "chalk": "^2.3.0", + "chalk": "^2.4.2", "source-map": "^0.6.1", - "supports-color": "^4.4.0" + "supports-color": "^6.1.0" }, "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "^3.0.0" } } } }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -6555,44 +6463,6 @@ "read-pkg": "^3.0.0" } }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "readdirp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", @@ -6717,19 +6587,15 @@ "dev": true }, "sanitize-html": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.16.1.tgz", - "integrity": "sha512-w3++cRkD2krVl8Zn70l7OcrF+zQc6lF0EVzCrcyFA3LR3AofZb2AuC3HRWyyNq225kSvl5K7IxSpQMkTQ+bHkw==", + "version": "1.27.5", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.5.tgz", + "integrity": "sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==", "dev": true, "requires": { - "htmlparser2": "^3.9.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.isarray": "^4.0.0", - "lodash.mergewith": "^4.6.0", - "postcss": "^6.0.14", - "srcset": "^1.0.0", - "xtend": "^4.0.0" + "htmlparser2": "^4.1.0", + "lodash": "^4.17.15", + "parse-srcset": "^1.0.2", + "postcss": "^7.0.27" } }, "semver": { @@ -6863,6 +6729,12 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "spawn-wrap": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", @@ -6915,16 +6787,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "srcset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", - "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", - "dev": true, - "requires": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" - } - }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -7189,9 +7051,9 @@ "dev": true }, "underscore": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", - "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", "dev": true }, "unpipe": { @@ -7332,18 +7194,18 @@ "signal-exit": "^3.0.2" } }, + "ws": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", + "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==", + "requires": {} + }, "xmlcreate": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", "dev": true }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, "y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", diff --git a/package.json b/package.json index 0e78963..1adca02 100644 --- a/package.json +++ b/package.json @@ -53,13 +53,17 @@ "@types/express-serve-static-core": "^4.17.9", "@types/lodash": "^4.14.159", "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", "commander": "^2.20.3", + "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "JSONStream": "^1.3.5", "lodash": "^4.17.20", - "uuid": "^3.4.0" + "uuid": "^3.4.0", + "ws": "^7.4.5" }, "devDependencies": { "body-parser": "^1.19.0", diff --git a/promise/index.d.ts b/promise/index.d.ts index c65fb0f..e07e56e 100644 --- a/promise/index.d.ts +++ b/promise/index.d.ts @@ -4,6 +4,7 @@ import https = require('https'); import http = require('http'); import events = require('events'); import Stream = require('stream'); +import WebSocket = require('isomorphic-ws'); import * as connect from 'connect'; export interface UtilsJSONParseOptions { @@ -222,6 +223,7 @@ export declare class Server extends events.EventEmitter { https(options?: HttpsServerOptions): HttpsServer; tcp(options?: TcpServerOptions): TcpServer; tls(options?: TlsServerOptions): TlsServer; + websocket(options?: WebsocketServerOptions): WebsocketServer; middleware(options?: MiddlewareServerOptions): connect.HandleFunction; method(name: string, definition: MethodLike): void; @@ -266,6 +268,14 @@ declare class TlsServer extends tls.Server { constructor(server: Server, options?: TlsServerOptions); } +export interface WebsocketServerOptions extends ServerOptions, WebSocket.ServerOptions { + wss?: WebSocket.Server; +} + +declare class WebsocketServer { + constructor(server: Server, options?: WebsocketServerOptions); +} + type JSONParseReviver = (key: string, value: any) => any; type JSONStringifyReplacer = (key: string, value: any) => any; @@ -307,6 +317,16 @@ declare class HttpsClient extends Client { constructor(options?: HttpsClientOptions); } +export interface WebsocketClientOptions extends ClientOptions { + url?: string; + ws?: WebSocket; + timeout?: number; +} + +declare class WebsocketClient extends Client { + constructor(options?: WebsocketClientOptions); +} + export declare class Client extends events.EventEmitter { constructor(server: Server, options?: ClientOptions); constructor(options: ClientOptions); @@ -315,6 +335,7 @@ export declare class Client extends events.EventEmitter { static https(options?: HttpsClientOptions): HttpsClient; static tcp(options?: TcpClientOptions): TcpClient; static tls(options?: TlsClientOptions): TlsClient; + static websocket(options?: WebsocketClientOptions): WebsocketClient; request(method:string, params:RequestParamsLike, id:JSONRPCIDLike | undefined, shouldCall:false): JSONRPCRequest; request(method:string, params:RequestParamsLike, id?:JSONRPCIDLike): Promise; diff --git a/promise/lib/client/index.js b/promise/lib/client/index.js index f0e73e9..4964a35 100644 --- a/promise/lib/client/index.js +++ b/promise/lib/client/index.js @@ -36,12 +36,18 @@ PromiseClient.https = require('./https'); * @type PromiseClientTls * @static */ - PromiseClient.tls = require('./tls'); + /** * @type PromiseClientTcp * @static */ PromiseClient.tcp = require('./tcp'); +/** + * @type PromiseClientWebsocket + * @static + */ +PromiseClient.websocket = require('./websocket'); + module.exports = PromiseClient; diff --git a/promise/lib/client/websocket.js b/promise/lib/client/websocket.js new file mode 100644 index 0000000..b04c27c --- /dev/null +++ b/promise/lib/client/websocket.js @@ -0,0 +1,23 @@ +'use strict'; + +const promisify = require('es6-promisify'); +const jayson = require('../../../'); +const promiseUtils = require('../utils'); + +/** + * Constructor for a Jayson Promise Client Websocket + * @see Client + * @class PromiseClientWebsocket + * @extends ClientWebsocket + * @return {PromiseClientWebsocket} + */ +const PromiseClientWebsocket = function(options) { + if(!(this instanceof PromiseClientWebsocket)) { + return new PromiseClientWebsocket(options); + } + jayson.Client.websocket.apply(this, arguments); + this.request = promiseUtils.wrapClientRequestMethod(this.request.bind(this)); +}; +require('util').inherits(PromiseClientWebsocket, jayson.Client.websocket); + +module.exports = PromiseClientWebsocket; diff --git a/test/examples.test.js b/test/examples.test.js new file mode 100644 index 0000000..8dedad1 --- /dev/null +++ b/test/examples.test.js @@ -0,0 +1,65 @@ +'use strict'; + +const last = require('lodash/last'); +const { spawn } = require('child_process'); +const path = require('path'); +const should = require('should'); +const fs = require('fs'); + +describe.skip('jayson/examples', () => { + + const examplesPath = __dirname + '/../examples'; + const dirs = fs.readdirSync(examplesPath); + + const tests = dirs.map(dir => fs.readdirSync(path.join(examplesPath, dir)).map(name => { + if (name !== 'server.js' && name !== 'client.js') return; + const type = path.basename(name); + return { + test: dir, + path: path.join(examplesPath, dir, name), + type, + }; + }).filter(v => v)).reduce((tests, list) => { + if (!list || list.length < 2) return tests; + const { test } = list[0]; + const server = list.find(o => o.type === 'server.js'); + const client = list.find(o => o.type === 'client.js'); + return tests.concat([{name: test, server, client}]); + }, []); + + for (const test of tests) { + const { name, client, server } = test; + it(`should successfully run the example ${name}`, function (done) { + this.timeout(5000); + let serverExitCode, clientExitCode; + + const serverProc = spawn('node', [server.path], {timeout: 2000}); + + serverProc.stderr.pipe(process.stderr); + serverProc.stdout.pipe(process.stdout); + + serverProc.once('exit', code => { + serverExitCode = code; + }); + + const clientProc = spawn('node', [client.path], {timeout: 2000}); + + clientProc.stderr.pipe(process.stderr); + clientProc.stdout.pipe(process.stdout); + + clientProc.once('exit', code => { + clientExitCode = code; + }); + + const poller = setInterval(() => { + if (typeof serverExitCode !== 'undefined' && typeof clientExitCode !== 'undefined') { + should(clientExitCode).eql(0); + // should(serverExitCode).eql(0); + clearInterval(poller); + done(); + } + }, 200); + }); + } + +}); diff --git a/test/promise.test.js b/test/promise.test.js index 489f4da..367610f 100644 --- a/test/promise.test.js +++ b/test/promise.test.js @@ -41,7 +41,8 @@ describe('jayson/promise', function() { // auto-generated test-suite const suites = { - 'regular': { + + regular: { server: function(done) { done(); return new jaysonPromise.Server(support.server.methods(), support.server.options()); @@ -50,7 +51,8 @@ describe('jayson/promise', function() { return jaysonPromise.Client(server, support.server.options()); } }, - 'http': { + + http: { server: function(done) { const server = jaysonPromise.Server(support.server.methods(), support.server.options()); const http = server.http(); @@ -69,7 +71,8 @@ describe('jayson/promise', function() { http.close(done); } }, - 'https': { + + https: { server: function(done) { const server = jaysonPromise.Server(support.server.methods(), support.server.options()); const https = server.https(support.server.keys()); @@ -89,6 +92,7 @@ describe('jayson/promise', function() { https.close(done); } }, + browser: { server: function(done) { const server = jaysonPromise.Server(support.server.methods(), support.server.options()); @@ -118,7 +122,8 @@ describe('jayson/promise', function() { http.close(done); } }, - 'tcp': { + + tcp: { server: function(done) { const server = jaysonPromise.Server(support.server.methods(), support.server.options()); const tcp = server.tcp(); @@ -138,7 +143,8 @@ describe('jayson/promise', function() { tcp.close(done); } }, - 'tls': { + + tls: { server: function(done) { const server = jaysonPromise.Server(support.server.methods(), support.server.options()); const tls = server.tls(support.server.keys()); @@ -157,7 +163,28 @@ describe('jayson/promise', function() { closeServer: function(tls, done) { tls.close(done); } + }, + + websocket: { + server: function(done) { + const server = jaysonPromise.Server(support.server.methods(), support.server.options()); + const wss = server.websocket({port: 3999}); + done(); + return wss; + }, + client: function (server, done) { + const websocket = jaysonPromise.Client.websocket({ + url: 'ws://localhost:3999', + }); + websocket.ws.on('open', done); + return websocket; + }, + closeServer: function(wss, done) { + wss.close(); + done(); + } } + }; forEach(suites, function(suite, name) { @@ -170,8 +197,13 @@ describe('jayson/promise', function() { }); let client = null; - beforeEach(function() { - client = suite.client(server); + beforeEach(function(done) { + if (suite.client.length === 2) { + client = suite.client(server, done); + } else { + client = suite.client(server); + done(); + } }); if(suite.closeServer) { diff --git a/test/support/suites.js b/test/support/suites.js index 0109274..6fe08d5 100644 --- a/test/support/suites.js +++ b/test/support/suites.js @@ -8,16 +8,22 @@ const http = require('http'); /** * Get a mocha suite for common test cases for a client - * @param {Client} Client instance to use + * @param {Client|Function} outerClient Client instance to use * @param {Boolean} [options.instanceOfClient=true] When false, don't check if the client is an instance of Client + * @param {Boolean} [options.getClient=false] When true client will be derived by executing client() * @return {Function} */ -exports.getCommonForClient = function(client, options) { - options = options || {instanceOfClient: true}; +exports.getCommonForClient = function (outerClient, options = {}) { + const { instanceOfClient = true, getClient = false } = options; return function() { - if(options.instanceOfClient) { + let client; + before(() => { + client = getClient ? outerClient() : outerClient; + }); + + if(instanceOfClient) { it('should be an instance of jayson.Client', function() { client.should.be.instanceof(jayson.Client); }); diff --git a/test/tcp.client-server.test.js b/test/tcp.client-server.test.js index 203e179..b2dee8e 100644 --- a/test/tcp.client-server.test.js +++ b/test/tcp.client-server.test.js @@ -14,7 +14,7 @@ describe('jayson.tcp', function() { let server = null; before(function() { - server = jayson.server(support.server.methods(), support.server.options()).tcp(); + server = new jayson.server(support.server.methods(), support.server.options()).tcp(); }); after(function() { @@ -31,7 +31,7 @@ describe('jayson.tcp', function() { server.should.be.instanceof(net.Server); }); - context('connected socket', function() { + describe('connected socket', function() { let socket = null; let responses = null; diff --git a/test/websocket.server-client.test.js b/test/websocket.server-client.test.js new file mode 100644 index 0000000..a94955c --- /dev/null +++ b/test/websocket.server-client.test.js @@ -0,0 +1,71 @@ +'use strict'; + +const should = require('should'); +const jayson = require('./../'); +const support = require('./support'); +const suites = require('./support/suites'); +const WebSocket = require('isomorphic-ws'); + +describe('jayson.websocket', function() { + + describe('server', function() { + + let server, serverWebsocket; + before(function() { + server = jayson.server(support.server.methods(), support.server.options()); + serverWebsocket = server.websocket({port: 3999}); + }); + + it('should be an instance of WebSocket.Server', function () { + should(serverWebsocket).be.an.instanceof(WebSocket.Server); + }); + + after(function() { + serverWebsocket.close(); + }); + + }); + + describe('client', function() { + + let client, server, serverWebsocket; + before(function (done) { + server = new jayson.server(support.server.methods(), support.server.options()); + serverWebsocket = server.websocket({port: 3999}); + client = jayson.client.websocket({ + url: 'ws://localhost:3999', + reviver: support.server.options().reviver, + replacer: support.server.options().replacer, + }); + client.ws.on('open', done); + }); + + after(function() { + client.ws.close(); + serverWebsocket.close(); + }); + + describe('common tests', suites.getCommonForClient(() => client, { + getClient: true, + })); + + describe('timeout', function () { + + it('should timeout of timeout in options', function (done) { + client.options.timeout = 5; + client.request('add_slow', [1, 2, true], function (err, result) { + should(err).be.ok(); + should(err).have.property('message', 'timeout reached after 5 ms'); + done(); + }); + }); + + it('should have zero outstanding requests', function () { + should(client).have.property('outstandingRequests').eql([]); + }); + + }); + + }); + +}); diff --git a/typescript/test.ts b/typescript/test.ts index 3900ecd..ce198c8 100644 --- a/typescript/test.ts +++ b/typescript/test.ts @@ -4,6 +4,7 @@ import jaysonBrowserClient from './../lib/client/browser'; import jaysonPromiseBrowserClient from './../promise/lib/client/browser'; import { reduce, isArray } from 'lodash'; import { Express } from 'express-serve-static-core'; +import WebSocket from 'isomorphic-ws'; /** * This file contains tests for the typescript type definitions. @@ -794,6 +795,8 @@ export function test_clientBrowserPromise () { export function test_server_and_method_call () { const server = new jayson.Server(); const request = jayson.Utils.request('add', [1, 2], undefined, {generator: () => Math.random()}); + server.call(request, {}, function (err, result) {}); + server.call(request, function (err, result) {}); server.call(request, {}, function (err?:jayson.JSONRPCResponseWithError | null, result?: jayson.JSONRPCResponseWithResult) { if (err) { console.log(err.error); @@ -802,3 +805,32 @@ export function test_server_and_method_call () { } }); } + +export function test_websocket () { + const wss = new WebSocket.Server({port: 12345}); + const server = new jayson.Server(); + const websocketServer = server.websocket({wss}); + + const ws = new WebSocket('ws://localhost:12345'); + const websocketClient = jayson.Client.websocket({ + url: 'ws://localhost:12345', + ws, + timeout: 5000, + }); +} + +export async function test_websocket_Promise () { + const wss = new WebSocket.Server({port: 12345}); + const server = new jaysonPromise.Server(); + const websocketServer = server.websocket({wss}); + + const ws = new WebSocket('ws://localhost:12345'); + const websocketClient = jaysonPromise.Client.websocket({ + url: 'ws://localhost:12345', + ws, + timeout: 5000, + }); + + const result = await websocketClient.request('add', [1,2,3]); +} +>>>>>>> websocket