diff options
Diffstat (limited to '')
48 files changed, 1541 insertions, 2701 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 27811ba..20ee736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,25 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [2.1.92 pre 2.2] - 2019-11-06 +### Added +- (Server) `./server/src/utility/` directory +- (Server) `Constants.js` class in `utility` +- (Server) `esm` module to transpile ES6 + +### Changed +- (Server) Changed ES5 styling to ES6 +- (Server) And improved source comments +- (Server) Minor code format changes +- (Server) Updated all dependencies (be sure to update your local copy with the new packages) + ## [2.1.91 pre 2.2] - 2019-08-17 ### Added - (Client) Markdown engine - (Client) Imgur based image posting (through markdown) ### Changed -- (Client) Removed cloudflare references making hack.chat is self-hosted again +- (Client) Removed cloudflare references making hack.chat self-hosted again - (Client) The way messages are pushed, closing an xss vuln in PRs 985dd6f and 9fcb235 - (Client) Side bar layout - (Client) Fixed some options not storing diff --git a/clientSource/package-lock.json b/clientSource/package-lock.json index b08a515..85b9e00 100644 --- a/clientSource/package-lock.json +++ b/clientSource/package-lock.json @@ -5,9 +5,12 @@ "requires": true, "dependencies": { "async": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz", - "integrity": "sha1-rDYTsdqb7RtHUQu0ZRuJMeRxRsc=" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } }, "colors": { "version": "1.0.3", @@ -28,14 +31,14 @@ } }, "ecstatic": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-1.4.1.tgz", - "integrity": "sha1-Mst7b6LikNWGaGdNEV6PDD1WfWo=", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", + "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", "requires": { - "he": "^0.5.0", - "mime": "^1.2.11", + "he": "^1.1.1", + "mime": "^1.6.0", "minimist": "^1.1.0", - "url-join": "^1.0.0" + "url-join": "^2.0.5" } }, "eventemitter3": { @@ -52,9 +55,9 @@ } }, "he": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/he/-/he-0.5.0.tgz", - "integrity": "sha1-LAX/rvkLaOhg8/0rVO9YCYknfuI=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, "http-proxy": { "version": "1.18.0", @@ -67,20 +70,25 @@ } }, "http-server": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.9.0.tgz", - "integrity": "sha1-jxsGvcczYY1NxCgxx7oa/04GABo=", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.11.1.tgz", + "integrity": "sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w==", "requires": { "colors": "1.0.3", "corser": "~2.0.0", - "ecstatic": "^1.4.0", + "ecstatic": "^3.0.0", "http-proxy": "^1.8.1", "opener": "~1.4.0", "optimist": "0.6.x", - "portfinder": "0.4.x", + "portfinder": "^1.0.13", "union": "~0.4.3" } }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -133,12 +141,13 @@ } }, "portfinder": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-0.4.0.tgz", - "integrity": "sha1-o/+t/6/k+5jgYBqF7aJ8J86Eyh4=", + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", + "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", "requires": { - "async": "0.9.0", - "mkdirp": "0.5.x" + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" } }, "qs": { @@ -160,9 +169,9 @@ } }, "url-join": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz", - "integrity": "sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg=" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" }, "wordwrap": { "version": "0.0.3", diff --git a/clientSource/package.json b/clientSource/package.json index 7abba43..63df0d3 100644 --- a/clientSource/package.json +++ b/clientSource/package.json @@ -17,6 +17,6 @@ "author": "Marzavec", "license": "WTFPL", "dependencies": { - "http-server": "^0.9.0" + "http-server": "^0.11.1" } } diff --git a/package-lock.json b/package-lock.json index d79ef14..5ba3eb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,6 +55,16 @@ "proxy-agent": "^3.1.0", "semver": "^5.5.0", "ws": "^5.1.0" + }, + "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + } } }, "@pm2/agent-node": { @@ -68,6 +78,14 @@ "ws": "^6.0.0" }, "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, "ws": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", @@ -97,6 +115,14 @@ "tslib": "1.9.3" }, "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -129,6 +155,14 @@ "ws": "^3.0.0" }, "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -147,11 +181,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "ws": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", @@ -199,22 +228,12 @@ } }, "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "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=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, "argparse": { @@ -232,48 +251,15 @@ } } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "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==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, "ast-types": { "version": "0.13.2", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.2.tgz", "integrity": "sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==" }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, - "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==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz", + "integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ==" }, "async-limiter": { "version": "1.0.1", @@ -289,11 +275,6 @@ "shimmer": "^1.1.0" } }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, "axios": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", @@ -308,60 +289,10 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "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=", - "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==", - "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==", - "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==", - "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==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" }, "blessed": { "version": "0.1.81", @@ -383,30 +314,11 @@ } }, "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "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=", - "requires": { - "is-extendable": "^0.1.0" - } - } + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" } }, "buffer-from": { @@ -419,22 +331,6 @@ "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==", - "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" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -451,43 +347,18 @@ "integrity": "sha1-BsIe7RobBq62dVPNxT4jJ0usIpY=" }, "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "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" - } - }, - "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==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", "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=", - "requires": { - "is-descriptor": "^0.1.0" - } - } + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" } }, "cli-table-redemption": { @@ -527,15 +398,6 @@ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -554,11 +416,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" }, - "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==" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -573,20 +430,15 @@ "emitter-listener": "^1.1.1" } }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, "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=" }, "cron": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/cron/-/cron-1.7.2.tgz", - "integrity": "sha512-+SaJ2OfeRvfQqwXQ2kgr0Y5pzBR/lijf5OpnnaruwWnmI799JfWr2jN2ItOV9s3A/+TFOt6mxvKzQq5F0Jp6VQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/cron/-/cron-1.7.1.tgz", + "integrity": "sha512-gmMB/pJcqUVs/NklR1sCGlNYM7TizEw+1gebz20BMc/8bTm/r7QUp3ZPSPlG8Z5XRlvb7qhjEjq/+bdIfUCL2A==", "requires": { "moment-timezone": "^0.5.x" } @@ -597,9 +449,9 @@ "integrity": "sha1-lQL18BVKLVoioCPnn3HMk2+m728=" }, "data-uri-to-buffer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", - "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", + "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==" }, "date-fns": { "version": "1.30.1", @@ -607,60 +459,18 @@ "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==" }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { "ms": "^2.1.1" } }, - "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=" - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "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==", - "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==", - "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==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "degenerator": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", @@ -719,6 +529,11 @@ "source-map": "~0.6.1" } }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "esprima": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", @@ -744,134 +559,11 @@ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", "integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI=" }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "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": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "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=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "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": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "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=", - "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==", - "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==", - "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==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -888,24 +580,11 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "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=", - "requires": { - "is-extendable": "^0.1.0" - } - } + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" } }, "follow-redirects": { @@ -931,504 +610,16 @@ } } }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "^0.2.2" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": 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, - "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, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": 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, - "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, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": 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, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": 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, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": 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, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": 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, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": 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, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "optional": true - } - } + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.1.tgz", + "integrity": "sha512-4FRPXWETxtigtJW/gxzEDsX1LVbPAM93VleB83kZB+ellqbHMkyt2aJfuzNLRvFPnGi6bcE5SvfxgbXPeKteJw==", + "optional": true }, "ftp": { "version": "0.3.10", @@ -1453,33 +644,33 @@ } }, "get-uri": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.3.tgz", - "integrity": "sha512-x5j6Ks7FOgLD/GlvjKwgu7wdmMR55iuRHhn8hj/+gA+eSbxQvZ+AEomq+3MgVEZj1vpi738QahGbCCSIDtXtkw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.4.tgz", + "integrity": "sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q==", "requires": { - "data-uri-to-buffer": "2", - "debug": "4", + "data-uri-to-buffer": "1", + "debug": "2", "extend": "~3.0.2", "file-uri-to-path": "1", "ftp": "~0.3.10", - "readable-stream": "3" + "readable-stream": "2" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { - "ms": "^2.1.1" + "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, "git-node-fs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz", @@ -1491,9 +682,9 @@ "integrity": "sha1-WZrBkrcYdYJeE6RF86bgURjC90U=" }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1504,29 +695,13 @@ } }, "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "requires": { - "is-glob": "^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=", - "requires": { - "is-extglob": "^2.1.0" - } - } + "is-glob": "^4.0.1" } }, - "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -1540,40 +715,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "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=", - "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==" - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "http-errors": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", @@ -1611,12 +752,22 @@ } }, "https-proxy-agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", - "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", + "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", "requires": { "agent-base": "^4.3.0", "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } } }, "iconv-lite": { @@ -1656,35 +807,12 @@ "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, - "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=", - "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==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "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=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "^2.0.0" } }, "is-buffer": { @@ -1692,51 +820,6 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" }, - "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=", - "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==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "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==" - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1751,51 +834,15 @@ } }, "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "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==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "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==", - "requires": { - "isobject": "^3.0.1" - } - }, - "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==" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, "js-git": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz", @@ -1807,11 +854,6 @@ "pako": "^0.2.5" } }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, "lazy": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", @@ -1827,9 +869,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==" }, "lodash.findindex": { "version": "4.6.0", @@ -1857,45 +899,11 @@ "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==" }, "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==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { - "object-visit": "^1.0.0" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "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" + "yallist": "^3.0.2" } }, "minimatch": { @@ -1911,25 +919,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "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==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -1949,9 +938,9 @@ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" }, "moment-timezone": { - "version": "0.5.26", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.26.tgz", - "integrity": "sha512-sFP4cgEKTCymBBKgoxZjYzlSovC20Y6J7y3nanDc5RoBIXKlZhoYwBoZGe3flwU6A372AcRwScH8KiwV6zjy1g==", + "version": "0.5.27", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz", + "integrity": "sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==", "requires": { "moment": ">= 2.9.0" } @@ -1966,30 +955,6 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "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" - } - }, "needle": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", @@ -1998,6 +963,16 @@ "debug": "^3.2.6", "iconv-lite": "^0.4.4", "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } } }, "netmask": { @@ -2026,55 +1001,6 @@ } } }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "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=", - "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==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2084,28 +1010,28 @@ } }, "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==", "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" + "word-wrap": "~1.2.3" } }, "pac-proxy-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.0.tgz", - "integrity": "sha512-AOUX9jES/EkQX2zRz0AW7lSx9jD//hQS8wFXBvcnd/J2Py9KaMJMqV/LPqJssj1tgGufotb2mmopGPR15ODv1Q==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz", + "integrity": "sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ==", "requires": { "agent-base": "^4.2.0", - "debug": "^3.1.0", + "debug": "^4.1.1", "get-uri": "^2.0.0", "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", + "https-proxy-agent": "^3.0.0", "pac-resolver": "^3.0.0", "raw-body": "^2.2.0", "socks-proxy-agent": "^4.0.1" @@ -2128,16 +1054,6 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2148,6 +1064,11 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, + "picomatch": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", + "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==" + }, "pidusage": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.17.tgz", @@ -2157,40 +1078,42 @@ } }, "pm2": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/pm2/-/pm2-3.5.1.tgz", - "integrity": "sha512-pDPBetbI48JDynxmtSPNSKRDk/LRbhsHrJ/WSCKVD1TUNnb6XNMe6GzO7DCNijJBvdaQ+u1WxNudxqExlXdhEw==", - "requires": { - "@pm2/agent": "^0.5.22", - "@pm2/io": "^4.1.2", - "@pm2/js-api": "^0.5.43", - "async": "^2.6.1", - "blessed": "^0.1.81", - "chalk": "^2.4.1", - "chokidar": "^2.0.4", - "cli-table-redemption": "^1.0.0", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-4.1.2.tgz", + "integrity": "sha512-HkNP3K79PmqxkxfgNX/t5rsfWLNMc89KVKDm2Xoju0MU3TbGZSc2rWLZffgEitITsMcPpkKtZmAJBcxMXvURFA==", + "requires": { + "@pm2/agent": "^0.5.26", + "@pm2/io": "^4.3.2", + "@pm2/js-api": "^0.5.60", + "async": "^3.1.0", + "blessed": "0.1.81", + "chalk": "2.4.2", + "chokidar": "^3.2.0", + "cli-table-redemption": "1.0.1", "commander": "2.15.1", - "cron": "^1.3", - "date-fns": "^1.29.0", - "debug": "^3.1", + "cron": "1.7.1", + "date-fns": "1.30.1", + "debug": "4.1.1", "eventemitter2": "5.0.1", "fclone": "1.0.11", + "lodash": "4.17.14", "mkdirp": "0.5.1", - "moment": "^2.22.2", - "needle": "^2.2.1", - "pidusage": "^2.0.14", + "moment": "2.24.0", + "needle": "2.4.0", + "pidusage": "2.0.17", "pm2-axon": "3.3.0", - "pm2-axon-rpc": "^0.5.1", + "pm2-axon-rpc": "0.5.1", "pm2-deploy": "^0.4.0", "pm2-multimeter": "^0.1.2", "promptly": "^2", + "ps-list": "6.3.0", "semver": "^5.5", - "shelljs": "~0.8.2", - "source-map-support": "^0.5.6", - "sprintf-js": "1.1.1", - "v8-compile-cache": "^2.0.0", + "shelljs": "0.8.3", + "source-map-support": "0.5.12", + "sprintf-js": "1.1.2", + "systeminformation": "^4.14.11", "vizion": "~2.0.2", - "yamljs": "^0.3.0" + "yamljs": "0.3.0" } }, "pm2-axon": { @@ -2202,6 +1125,16 @@ "amp-message": "~0.1.1", "debug": "^3.0", "escape-regexp": "0.0.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } } }, "pm2-axon-rpc": { @@ -2210,6 +1143,16 @@ "integrity": "sha512-hT8gN3/j05895QLXpwg+Ws8PjO4AVID6Uf9StWpud9HB2homjc1KKCcI0vg9BNOt56FmrqKDT1NQgheIz35+sA==", "requires": { "debug": "^3.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } } }, "pm2-deploy": { @@ -2219,6 +1162,16 @@ "requires": { "async": "^2.6", "tv4": "^1.3" + }, + "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + } } }, "pm2-multimeter": { @@ -2229,11 +1182,6 @@ "charm": "~0.1.1" } }, - "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=" - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -2253,16 +1201,16 @@ } }, "proxy-agent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.1.0.tgz", - "integrity": "sha512-IkbZL4ClW3wwBL/ABFD2zJ8iP84CY0uKMvBPk/OceQe/cEjrxzN1pMHsLwhbzUoRhG9QbSxYC+Z7LBkTiBNvrA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.1.1.tgz", + "integrity": "sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw==", "requires": { "agent-base": "^4.2.0", - "debug": "^3.1.0", + "debug": "4", "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "pac-proxy-agent": "^3.0.0", + "https-proxy-agent": "^3.0.0", + "lru-cache": "^5.1.1", + "pac-proxy-agent": "^3.0.1", "proxy-from-env": "^1.0.0", "socks-proxy-agent": "^4.0.1" } @@ -2272,10 +1220,10 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=" }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "ps-list": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ps-list/-/ps-list-6.3.0.tgz", + "integrity": "sha512-qau0czUSB0fzSlBOQt0bo+I2v6R+xiQdj78e1BR/Qjfl5OHWJ/urXi8+ilw1eHe+5hSeDI1wrwVTgDp2wst4oA==" }, "raw-body": { "version": "2.4.1", @@ -2297,33 +1245,17 @@ } }, "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "dependencies": { - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - } - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" }, "dependencies": { "isarray": { @@ -2331,25 +1263,6 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2360,6 +1273,14 @@ } } }, + "readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "requires": { + "picomatch": "^2.0.4" + } + }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -2368,30 +1289,6 @@ "resolve": "^1.1.6" } }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "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=" - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, "require-in-the-middle": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-4.0.1.tgz", @@ -2400,16 +1297,6 @@ "debug": "^4.1.1", "module-details-from-path": "^1.0.3", "resolve": "^1.12.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } } }, "resolve": { @@ -2420,28 +1307,10 @@ "path-parse": "^1.0.6" } }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "requires": { - "ret": "~0.1.10" - } + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", @@ -2458,27 +1327,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, - "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==", - "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=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", @@ -2505,137 +1353,17 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "smart-buffer": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", - "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==" - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "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": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "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=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "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==", - "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=", - "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==", - "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==", - "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==", - "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==", - "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==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" }, "socks": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.2.tgz", - "integrity": "sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", + "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", "requires": { - "ip": "^1.1.5", - "smart-buffer": "4.0.2" + "ip": "1.1.5", + "smart-buffer": "^4.1.0" } }, "socks-proxy-agent": { @@ -2662,63 +1390,19 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, - "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==", - "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.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", "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=" - }, - "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==", - "requires": { - "extend-shallow": "^3.0.0" - } - }, "sprintf-js": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", - "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.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=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, "statuses": { "version": "1.5.0", @@ -2746,52 +1430,23 @@ "has-flag": "^3.0.0" } }, + "systeminformation": { + "version": "4.14.17", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-4.14.17.tgz", + "integrity": "sha512-CQbT5vnkqNb3JNl41xr8sYA8AX7GoaWP55/jnmFNQY0XQmUuoFshSNUkCkxiDdEC1qu2Vg9s0jR6LLmVSmNJUw==", + "optional": true + }, "thunkify": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=" }, - "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=", - "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==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "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==", - "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=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" } }, "toidentifier": { @@ -2822,78 +1477,11 @@ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" }, - "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==", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, "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=", - "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=", - "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=", - "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=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2904,11 +1492,6 @@ "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", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==" - }, "vizion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/vizion/-/vizion-2.0.2.tgz", @@ -2934,10 +1517,10 @@ } } }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + "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==" }, "wrappy": { "version": "1.0.2", @@ -2958,9 +1541,9 @@ "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=" }, "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yamljs": { "version": "0.3.0", diff --git a/package.json b/package.json index 9edd245..b54c911 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,13 @@ "npm": ">= 6.7.0" }, "scripts": { - "start": "pm2 start ./server/main.js --name hackchat-dev-server && cd ./clientSource && npm start && pm2 stop hackchat-dev-server", + "start": "pm2 start ./server/main.js --node-args=\"-r esm\" --name hackchat-dev-server && cd ./clientSource && npm start && pm2 stop hackchat-dev-server", "postinstall": "cd ./clientSource && npm install && cd .. & cd ./server && npm install && npm run config" }, "author": "Marzavec", "license": "WTFPL", "dependencies": { - "pm2": "^3.5.1" + "esm": "^3.2.25", + "pm2": "^4.1.2" } } diff --git a/server/main.js b/server/main.js index 70042e8..b01d09d 100644 --- a/server/main.js +++ b/server/main.js @@ -1,15 +1,12 @@ /** * HackChat main server entry point - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * + * @author Marzavec ( https://github.com/marzavec ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) */ -'use strict'; - // import and initialize the core application -const CoreApp = require('./src/serverLib/CoreApp'); +import { CoreApp } from './src/serverLib/CoreApp'; + const coreApp = new CoreApp(); coreApp.init(); diff --git a/server/package-lock.json b/server/package-lock.json index 27205bc..26fd58b 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -18,9 +18,9 @@ "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" }, "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, "balanced-match": { "version": "1.0.0", @@ -104,6 +104,11 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "eyes": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", @@ -350,11 +355,11 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", - "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz", + "integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==", "requires": { - "async-limiter": "~1.0.0" + "async-limiter": "^1.0.0" } } } diff --git a/server/package.json b/server/package.json index 51a149d..3eb733c 100644 --- a/server/package.json +++ b/server/package.json @@ -12,8 +12,8 @@ "npm": ">= 5.7.1" }, "scripts": { - "start": "node main.js", - "config": "node src/scripts/configure.js" + "start": "node -r esm main.js", + "config": "node -r esm src/scripts/configure.js" }, "author": "Marzavec", "license": "WTFPL", @@ -22,9 +22,10 @@ "common-tags": "^1.8.0", "dateformat": "^3.0.3", "didyoumean2": "^2.0.2", + "esm": "^3.2.25", "fs-extra": "^7.0.1", "prompt": "^1.0.0", "readdir-recursive": "0.0.4", - "ws": "^6.1.4" + "ws": "^7.2.0" } } diff --git a/server/src/commands/admin/addmod.js b/server/src/commands/admin/addmod.js index 6853a42..26cec40 100644 --- a/server/src/commands/admin/addmod.js +++ b/server/src/commands/admin/addmod.js @@ -3,26 +3,26 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin - if (socket.uType != 'admin') { - return server.police.frisk(socket.remoteAddress, 20); + if (socket.uType !== 'admin') { + return server.police.frisk(socket.address, 20); } // add new trip to config core.config.mods.push({ trip: data.trip }); // find targets current connections - let newMod = server.findSockets({ trip: data.trip }); + const newMod = server.findSockets({ trip: data.trip }); if (newMod.length !== 0) { - for (let i = 0, l = newMod.length; i < l; i++) { + for (let i = 0, l = newMod.length; i < l; i += 1) { // upgrade privilages newMod[i].uType = 'mod'; // inform new mod server.send({ cmd: 'info', - text: 'You are now a mod.' + text: 'You are now a mod.', }, newMod[i]); } } @@ -30,21 +30,22 @@ exports.run = async (core, server, socket, data) => { // return success message server.reply({ cmd: 'info', - text: `Added mod trip: ${data.trip}, remember to run 'saveconfig' to make it permanent` + text: `Added mod trip: ${data.trip}, remember to run 'saveconfig' to make it permanent`, }, socket); // notify all mods server.broadcast({ cmd: 'info', - text: `Added mod: ${data.trip}` + text: `Added mod: ${data.trip}`, }, { uType: 'mod' }); -}; -// module meta -exports.requiredData = ['trip']; -exports.info = { + return true; +} + +export const requiredData = ['trip']; +export const info = { name: 'addmod', description: 'Adds target trip to the config as a mod and upgrades the socket type', usage: ` - API: { cmd: 'addmod', trip: '<target trip>' }` + API: { cmd: 'addmod', trip: '<target trip>' }`, }; diff --git a/server/src/commands/admin/listusers.js b/server/src/commands/admin/listusers.js index 5ff350f..0b0199f 100644 --- a/server/src/commands/admin/listusers.js +++ b/server/src/commands/admin/listusers.js @@ -3,46 +3,47 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket) { // increase rate limit chance and ignore if not admin - if (socket.uType != 'admin') { - return server.police.frisk(socket.remoteAddress, 20); + if (socket.uType !== 'admin') { + return server.police.frisk(socket.address, 20); } // find all users currently in a channel - let currentUsers = server.findSockets({ - channel: (channel) => true + const currentUsers = server.findSockets({ + channel: (channel) => true, }); // compile channel and user list - let channels = {}; - for (let i = 0, j = currentUsers.length; i < j; i++) { + const channels = {}; + for (let i = 0, j = currentUsers.length; i < j; i += 1) { if (typeof channels[currentUsers[i].channel] === 'undefined') { channels[currentUsers[i].channel] = []; } - + channels[currentUsers[i].channel].push( - `[${currentUsers[i].trip||'null'}]${currentUsers[i].nick}` + `[${currentUsers[i].trip || 'null'}]${currentUsers[i].nick}`, ); } // build output - let lines = []; - for (let channel in channels) { - lines.push(`?${channel} ${channels[channel].join(", ")}`); + const lines = []; + for (const channel in channels) { + lines.push(`?${channel} ${channels[channel].join(', ')}`); } // send reply server.reply({ cmd: 'info', - text: lines.join("\n") + text: lines.join('\n'), }, socket); -}; -// module meta -exports.info = { + return true; +} + +export const info = { name: 'listusers', description: 'Outputs all current channels and sockets in those channels', usage: ` - API: { cmd: 'listusers' }` + API: { cmd: 'listusers' }`, }; diff --git a/server/src/commands/admin/reload.js b/server/src/commands/admin/reload.js index 588d1c5..206e2ca 100644 --- a/server/src/commands/admin/reload.js +++ b/server/src/commands/admin/reload.js @@ -3,10 +3,10 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin - if (socket.uType != 'admin') { - return server.police.frisk(socket.remoteAddress, 20); + if (socket.uType !== 'admin') { + return server.police.frisk(socket.address, 20); } // do command reload and store results @@ -17,7 +17,7 @@ exports.run = async (core, server, socket, data) => { server.loadHooks(); // build reply based on reload results - if (loadResult == '') { + if (loadResult === '') { loadResult = `Reloaded ${core.commands.commands.length} commands, 0 errors`; } else { loadResult = `Reloaded ${core.commands.commands.length} commands, error(s): @@ -31,20 +31,21 @@ exports.run = async (core, server, socket, data) => { // reply with results server.reply({ cmd: 'info', - text: loadResult + text: loadResult, }, socket); // notify mods of reload #transparency server.broadcast({ cmd: 'info', - text: loadResult + text: loadResult, }, { uType: 'mod' }); -}; -// module meta -exports.info = { + return true; +} + +export const info = { name: 'reload', description: '(Re)loads any new commands into memory, outputs errors if any', usage: ` - API: { cmd: 'reload', reason: '<optional reason append>' }` + API: { cmd: 'reload', reason: '<optional reason append>' }`, }; diff --git a/server/src/commands/admin/removemod.js b/server/src/commands/admin/removemod.js index a2d862c..9190dd6 100644 --- a/server/src/commands/admin/removemod.js +++ b/server/src/commands/admin/removemod.js @@ -3,26 +3,26 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin - if (socket.uType != 'admin') { - return server.police.frisk(socket.remoteAddress, 20); + if (socket.uType !== 'admin') { + return server.police.frisk(socket.address, 20); } // remove trip from config - core.config.mods = core.config.mods.filter(mod => mod.trip !== data.trip); + core.config.mods = core.config.mods.filter((mod) => mod.trip !== data.trip); // find targets current connections - let targetMod = server.findSockets({ trip: data.trip }); + const targetMod = server.findSockets({ trip: data.trip }); if (targetMod.length !== 0) { - for (let i = 0, l = targetMod.length; i < l; i++) { + for (let i = 0, l = targetMod.length; i < l; i += 1) { // downgrade privilages targetMod[i].uType = 'user'; // inform ex-mod server.send({ cmd: 'info', - text: 'You are now a user.' + text: 'You are now a user.', }, targetMod[i]); } } @@ -32,21 +32,22 @@ exports.run = async (core, server, socket, data) => { cmd: 'info', text: `Removed mod trip: ${ data.trip - }, remember to run 'saveconfig' to make it permanent` + }, remember to run 'saveconfig' to make it permanent`, }, socket); // notify all mods server.broadcast({ cmd: 'info', - text: `Removed mod: ${data.trip}` + text: `Removed mod: ${data.trip}`, }, { uType: 'mod' }); -}; -// module meta -exports.requiredData = ['trip']; -exports.info = { + return true; +} + +export const requiredData = ['trip']; +export const info = { name: 'removemod', description: 'Removes target trip from the config as a mod and downgrades the socket type', usage: ` - API: { cmd: 'removemod', trip: '<target trip>' }` + API: { cmd: 'removemod', trip: '<target trip>' }`, }; diff --git a/server/src/commands/admin/saveconfig.js b/server/src/commands/admin/saveconfig.js index 65fff0e..6c713b4 100644 --- a/server/src/commands/admin/saveconfig.js +++ b/server/src/commands/admin/saveconfig.js @@ -3,37 +3,38 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket) { // increase rate limit chance and ignore if not admin - if (socket.uType != 'admin') { - return server.police.frisk(socket.remoteAddress, 20); + if (socket.uType !== 'admin') { + return server.police.frisk(socket.address, 20); } // attempt save, notify of failure if (!core.configManager.save()) { return server.reply({ cmd: 'warn', - text: 'Failed to save config, check logs.' - }, client); + text: 'Failed to save config, check logs.', + }, socket); } // return success message server.reply({ cmd: 'info', - text: 'Config saved!' + text: 'Config saved!', }, socket); // notify mods #transparency server.broadcast({ cmd: 'info', - text: 'Config saved!' + text: 'Config saved!', }, { uType: 'mod' }); -}; -// module meta -exports.info = { + return true; +} + +export const info = { name: 'saveconfig', description: 'Writes the current config to disk', usage: ` - API: { cmd: 'saveconfig' }` + API: { cmd: 'saveconfig' }`, }; diff --git a/server/src/commands/admin/shout.js b/server/src/commands/admin/shout.js index 821a22a..73b0734 100644 --- a/server/src/commands/admin/shout.js +++ b/server/src/commands/admin/shout.js @@ -3,24 +3,25 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin - if (socket.uType != 'admin') { - return server.police.frisk(socket.remoteAddress, 20); + if (socket.uType !== 'admin') { + return server.police.frisk(socket.address, 20); } // send text to all channels server.broadcast({ cmd: 'info', - text: `Server Notice: ${data.text}` + text: `Server Notice: ${data.text}`, }, {}); -}; -// module meta -exports.requiredData = ['text']; -exports.info = { + return true; +} + +export const requiredData = ['text']; +export const info = { name: 'shout', description: 'Displays passed text to every client connected', usage: ` - API: { cmd: 'shout', text: '<shout text>' }` + API: { cmd: 'shout', text: '<shout text>' }`, }; diff --git a/server/src/commands/core/changenick.js b/server/src/commands/core/changenick.js index cb6d1d0..632da83 100644 --- a/server/src/commands/core/changenick.js +++ b/server/src/commands/core/changenick.js @@ -6,43 +6,43 @@ const verifyNickname = (nick) => /^[a-zA-Z0-9_]{1,24}$/.test(nick); // module main -exports.run = async (core, server, socket, data) => { - if (server.police.frisk(socket.remoteAddress, 6)) { +export async function run(core, server, socket, data) { + if (server.police.frisk(socket.address, 6)) { return server.reply({ cmd: 'warn', - text: 'You are changing nicknames too fast. Wait a moment before trying again.' + text: 'You are changing nicknames too fast. Wait a moment before trying again.', }, socket); } // verify user data is string if (typeof data.nick !== 'string') { - return; + return true; } // make sure requested nickname meets standards - let newNick = data.nick.trim(); + const newNick = data.nick.trim(); if (!verifyNickname(newNick)) { return server.reply({ cmd: 'warn', - text: 'Nickname must consist of up to 24 letters, numbers, and underscores' + text: 'Nickname must consist of up to 24 letters, numbers, and underscores', }, socket); } // prevent admin impersonation // TODO: prevent mod impersonation - if (newNick.toLowerCase() == core.config.adminName.toLowerCase()) { - server.police.frisk(socket.remoteAddress, 4); + if (newNick.toLowerCase() === core.config.adminName.toLowerCase()) { + server.police.frisk(socket.address, 4); return server.reply({ cmd: 'warn', - text: 'You are not the admin, liar!' + text: 'You are not the admin, liar!', }, socket); } // find any sockets that have the same nickname - let userExists = server.findSockets({ + const userExists = server.findSockets({ channel: socket.channel, - nick: (targetNick) => targetNick.toLowerCase() === newNick.toLowerCase() + nick: (targetNick) => targetNick.toLowerCase() === newNick.toLowerCase(), }); // return error if found @@ -50,82 +50,83 @@ exports.run = async (core, server, socket, data) => { // That nickname is already in that channel return server.reply({ cmd: 'warn', - text: 'Nickname taken' + text: 'Nickname taken', }, socket); } // build join and leave notices // TODO: this is a legacy client holdover, name changes in the future will // have thieir own event - let leaveNotice = { + const leaveNotice = { cmd: 'onlineRemove', - nick: socket.nick + nick: socket.nick, }; - let joinNotice = { + const joinNotice = { cmd: 'onlineAdd', nick: newNick, trip: socket.trip || 'null', - hash: socket.hash + hash: socket.hash, }; // broadcast remove event and join event with new name, this is to support legacy clients and bots - server.broadcast( leaveNotice, { channel: socket.channel }); - server.broadcast( joinNotice, { channel: socket.channel }); + server.broadcast(leaveNotice, { channel: socket.channel }); + server.broadcast(joinNotice, { channel: socket.channel }); // notify channel that the user has changed their name - server.broadcast( { + server.broadcast({ cmd: 'info', - text: `${socket.nick} is now ${newNick}` + text: `${socket.nick} is now ${newNick}`, }, { channel: socket.channel }); // commit change to nickname socket.nick = newNick; -}; + + return true; +} // module hook functions -exports.initHooks = (server) => { +export function initHooks(server) { server.registerHook('in', 'chat', this.nickCheck, 29); -}; +} // hooks chat commands checking for /nick -exports.nickCheck = (core, server, socket, payload) => { +export function nickCheck(core, server, socket, payload) { if (typeof payload.text !== 'string') { return false; } if (payload.text.startsWith('/nick')) { - let input = payload.text.split(' '); + const input = payload.text.split(' '); // If there is no nickname target parameter if (input[1] === undefined) { server.reply({ cmd: 'warn', - text: 'Refer to `/help nick` for instructions on how to use this command.' + text: 'Refer to `/help nick` for instructions on how to use this command.', }, socket); return false; } - let newNick = input[1].replace(/@/g, ''); + const newNick = input[1].replace(/@/g, ''); this.run(core, server, socket, { cmd: 'changenick', - nick: newNick + nick: newNick, }); return false; } return payload; -}; +} -// module meta -exports.requiredData = ['nick']; -exports.info = { +export const requiredData = ['nick']; +export const info = { name: 'changenick', description: 'This will change your current connections nickname', usage: ` API: { cmd: 'changenick', nick: '<new nickname>' } - Text: /nick <new nickname>` + Text: /nick <new nickname>`, }; diff --git a/server/src/commands/core/chat.js b/server/src/commands/core/chat.js index 7b7e79e..8d0098b 100644 --- a/server/src/commands/core/chat.js +++ b/server/src/commands/core/chat.js @@ -9,43 +9,45 @@ const parseText = (text) => { return false; } + let sanitizedText = text; + // strip newlines from beginning and end - text = text.replace(/^\s*\n|^\s+$|\n\s*$/g, ''); + sanitizedText = sanitizedText.replace(/^\s*\n|^\s+$|\n\s*$/g, ''); // replace 3+ newlines with just 2 newlines - text = text.replace(/\n{3,}/g, "\n\n"); + sanitizedText = sanitizedText.replace(/\n{3,}/g, '\n\n'); - return text; + return sanitizedText; }; // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // check user input - let text = parseText(data.text); + const text = parseText(data.text); if (!text) { // lets not send objects or empty text, yea? - return server.police.frisk(socket.remoteAddress, 13); + return server.police.frisk(socket.address, 13); } // check for spam - let score = text.length / 83 / 4; - if (server.police.frisk(socket.remoteAddress, score)) { + const score = text.length / 83 / 4; + if (server.police.frisk(socket.address, score)) { return server.reply({ cmd: 'warn', - text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.' + text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.', }, socket); } // build chat payload - let payload = { + const payload = { cmd: 'chat', nick: socket.nick, - text: text + text, }; - if (socket.uType == 'admin') { + if (socket.uType === 'admin') { payload.admin = true; - } else if (socket.uType == 'mod') { + } else if (socket.uType === 'mod') { payload.mod = true; } @@ -54,20 +56,22 @@ exports.run = async (core, server, socket, data) => { } // broadcast to channel peers - server.broadcast( payload, { channel: socket.channel}); + server.broadcast(payload, { channel: socket.channel }); // stats are fun core.stats.increment('messages-sent'); -}; + + return true; +} // module hook functions -exports.initHooks = (server) => { +export function initHooks(server) { server.registerHook('in', 'chat', this.commandCheckIn, 20); server.registerHook('in', 'chat', this.finalCmdCheck, 254); -}; +} // checks for miscellaneous '/' based commands -exports.commandCheckIn = (core, server, socket, payload) => { +export function commandCheckIn(core, server, socket, payload) { if (typeof payload.text !== 'string') { return false; } @@ -75,16 +79,16 @@ exports.commandCheckIn = (core, server, socket, payload) => { if (payload.text.startsWith('/myhash')) { server.reply({ cmd: 'info', - text: `Your hash: ${socket.hash}` + text: `Your hash: ${socket.hash}`, }, socket); return false; } return payload; -}; +} -exports.finalCmdCheck = (core, server, socket, payload) => { +export function finalCmdCheck(core, server, socket, payload) { if (typeof payload.text !== 'string') { return false; } @@ -97,26 +101,23 @@ exports.finalCmdCheck = (core, server, socket, payload) => { payload.text = payload.text.substr(1); return payload; - } else { - server.reply({ - cmd: 'warn', - text: `Unknown command: ${payload.text}` - }, socket); - - return false; } - return payload; -}; + server.reply({ + cmd: 'warn', + text: `Unknown command: ${payload.text}`, + }, socket); + + return false; +} -// module meta -exports.requiredData = ['text']; -exports.info = { +export const requiredData = ['text']; +export const info = { name: 'chat', description: 'Broadcasts passed `text` field to the calling users channel', usage: ` API: { cmd: 'chat', text: '<text to send>' } Text: Uuuuhm. Just kind type in that little box at the bottom and hit enter.\n Bonus super secret hidden commands: - /myhash` + /myhash`, }; diff --git a/server/src/commands/core/emote.js b/server/src/commands/core/emote.js index b0203cc..f244d74 100644 --- a/server/src/commands/core/emote.js +++ b/server/src/commands/core/emote.js @@ -9,91 +9,94 @@ const parseText = (text) => { return false; } + let sanitizedText = text; + // strip newlines from beginning and end - text = text.replace(/^\s*\n|^\s+$|\n\s*$/g, ''); + sanitizedText = sanitizedText.replace(/^\s*\n|^\s+$|\n\s*$/g, ''); // replace 3+ newlines with just 2 newlines - text = text.replace(/\n{3,}/g, "\n\n"); + sanitizedText = sanitizedText.replace(/\n{3,}/g, '\n\n'); - return text; + return sanitizedText; }; // module main -exports.run = async (core, server, socket, payload) => { +export async function run(core, server, socket, payload) { // check user input - let text = parseText(payload.text); + const text = parseText(payload.text); if (!text) { // lets not send objects or empty text, yea? - return server.police.frisk(socket.remoteAddress, 8); + return server.police.frisk(socket.address, 8); } // check for spam - let score = text.length / 83 / 4; - if (server.police.frisk(socket.remoteAddress, score)) { + const score = text.length / 83 / 4; + if (server.police.frisk(socket.address, score)) { return server.reply({ cmd: 'warn', - text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.' + text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.', }, socket); } - let newPayload = { + const newPayload = { cmd: 'info', type: 'emote', nick: socket.nick, - text: `@${socket.nick} ${text}` + text: `@${socket.nick} ${text}`, }; if (socket.trip) { newPayload.trip = socket.trip; } // broadcast to channel peers - server.broadcast( newPayload, { channel: socket.channel}); -}; + server.broadcast(newPayload, { channel: socket.channel }); + + return true; +} // module hook functions -exports.initHooks = (server) => { +export function initHooks(server) { server.registerHook('in', 'chat', this.emoteCheck, 30); -}; +} // hooks chat commands checking for /me -exports.emoteCheck = (core, server, socket, payload) => { +export function emoteCheck(core, server, socket, payload) { if (typeof payload.text !== 'string') { return false; } if (payload.text.startsWith('/me ')) { - let input = payload.text.split(' '); + const input = payload.text.split(' '); // If there is no emote target parameter if (input[1] === undefined) { server.reply({ cmd: 'warn', - text: 'Refer to `/help emote` for instructions on how to use this command.' + text: 'Refer to `/help emote` for instructions on how to use this command.', }, socket); return false; } input.splice(0, 1); - let actionText = input.join(' '); + const actionText = input.join(' '); this.run(core, server, socket, { cmd: 'emote', - text: actionText + text: actionText, }); return false; } return payload; -}; +} -// module meta -exports.requiredData = ['text']; -exports.info = { +export const requiredData = ['text']; +export const info = { name: 'emote', description: 'Typical emote / action text', usage: ` API: { cmd: 'emote', text: '<emote/action text>' } - Text: /me <emote/action text>` + Text: /me <emote/action text>`, }; diff --git a/server/src/commands/core/help.js b/server/src/commands/core/help.js index 8eccdb6..25f8844 100644 --- a/server/src/commands/core/help.js +++ b/server/src/commands/core/help.js @@ -3,21 +3,21 @@ */ // module support functions -const stripIndents = require('common-tags').stripIndents; +const { stripIndents } = require('common-tags'); // module main -exports.run = async (core, server, socket, payload) => { +export async function run(core, server, socket, payload) { // check for spam - if (server.police.frisk(socket.remoteAddress, 2)) { + if (server.police.frisk(socket.address, 2)) { return server.reply({ cmd: 'warn', - text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.' + text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.', }, socket); } // verify user input if (typeof payload.command !== 'undefined' && typeof payload.command !== 'string') { - return; + return true; } let reply = ''; @@ -27,21 +27,21 @@ exports.run = async (core, server, socket, payload) => { API: {cmd: 'help', command: '<command name>'}`; reply += '\n\n-------------------------------------\n\n'; - let categories = core.commands.categoriesList.sort(); - for (let i = 0, j = categories.length; i < j; i++) { - reply += `${categories[i].replace('../src/commands/', '').replace(/^\w/, c => c.toUpperCase())} Commands:\n`; - let catCommands = core.commands.all(categories[i]).sort((a, b) => a.info.name.localeCompare(b.info.name)); - reply += ` ${catCommands.map(c => `${c.info.name}`).join(', ')}\n\n`; + const categories = core.commands.categoriesList.sort(); + for (let i = 0, j = categories.length; i < j; i += 1) { + reply += `${categories[i].replace('../src/commands/', '').replace(/^\w/, (c) => c.toUpperCase())} Commands:\n`; + const catCommands = core.commands.all(categories[i]).sort((a, b) => a.info.name.localeCompare(b.info.name)); + reply += ` ${catCommands.map((c) => `${c.info.name}`).join(', ')}\n\n`; } } else { - let command = core.commands.get(payload.command); + const command = core.commands.get(payload.command); if (typeof command === 'undefined') { reply = 'Unknown command'; } else { reply = stripIndents`Name: ${command.info.name} Aliases: ${typeof command.info.aliases !== 'undefined' ? command.info.aliases.join(', ') : 'None'} - Category: ${command.info.category.replace('../src/commands/', '').replace(/^\w/, c => c.toUpperCase())} + Category: ${command.info.category.replace('../src/commands/', '').replace(/^\w/, (c) => c.toUpperCase())} Required Parameters: ${command.requiredData || 'None'}\n Description: ${command.info.description || '¯\_(ツ)_/¯'}\n Usage: ${command.info.usage || command.info.name}`; @@ -51,40 +51,41 @@ exports.run = async (core, server, socket, payload) => { // output reply server.reply({ cmd: 'info', - text: reply + text: reply, }, socket); -}; + + return true; +} // module hook functions -exports.initHooks = (server) => { +export function initHooks(server) { server.registerHook('in', 'chat', this.helpCheck, 28); -}; +} // hooks chat commands checking for /whisper -exports.helpCheck = (core, server, socket, payload) => { +export function helpCheck(core, server, socket, payload) { if (typeof payload.text !== 'string') { return false; } if (payload.text.startsWith('/help')) { - let input = payload.text.substr(1).split(' ', 2); + const input = payload.text.substr(1).split(' ', 2); this.run(core, server, socket, { cmd: input[0], - command: input[1] + command: input[1], }); return false; } return payload; -}; +} -// module meta -exports.info = { +export const info = { name: 'help', description: 'Outputs information about the servers current protocol', usage: ` API: { cmd: 'help', command: '<optional command name>' } - Text: /help <optional command name>` + Text: /help <optional command name>`, }; diff --git a/server/src/commands/core/invite.js b/server/src/commands/core/invite.js index 70393b1..1811e8b 100644 --- a/server/src/commands/core/invite.js +++ b/server/src/commands/core/invite.js @@ -6,47 +6,47 @@ const verifyNickname = (nick) => /^[a-zA-Z0-9_]{1,24}$/.test(nick); // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // check for spam - if (server.police.frisk(socket.remoteAddress, 2)) { + if (server.police.frisk(socket.address, 2)) { return server.reply({ cmd: 'warn', - text: 'You are sending invites too fast. Wait a moment before trying again.' + text: 'You are sending invites too fast. Wait a moment before trying again.', }, socket); } // verify user input if (typeof data.nick !== 'string' || !verifyNickname(data.nick)) { - return; + return true; } // why would you invite yourself? - if (data.nick == socket.nick) { - return; + if (data.nick === socket.nick) { + return true; } // generate common channel - let channel = Math.random().toString(36).substr(2, 8); + const channel = Math.random().toString(36).substr(2, 8); // build and send invite - let payload = { + const payload = { cmd: 'info', type: 'invite', from: socket.nick, invite: channel, - text: `${socket.nick} invited you to ?${channel}` + text: `${socket.nick} invited you to ?${channel}`, }; - let inviteSent = server.broadcast( payload, { + const inviteSent = server.broadcast(payload, { channel: socket.channel, - nick: data.nick + nick: data.nick, }); // server indicates the user was not found if (!inviteSent) { return server.reply({ cmd: 'warn', - text: 'Could not find user in channel' + text: 'Could not find user in channel', }, socket); } @@ -55,18 +55,19 @@ exports.run = async (core, server, socket, data) => { cmd: 'info', type: 'invite', invite: channel, - text: `You invited ${data.nick} to ?${channel}` + text: `You invited ${data.nick} to ?${channel}`, }, socket); // stats are fun core.stats.increment('invites-sent'); -}; -// module meta -exports.requiredData = ['nick']; -exports.info = { + return true; +} + +export const requiredData = ['nick']; +export const info = { name: 'invite', description: 'Generates a unique (more or less) room name and passes it to two clients', usage: ` - API: { cmd: 'invite', nick: '<target nickname>' }` + API: { cmd: 'invite', nick: '<target nickname>' }`, }; diff --git a/server/src/commands/core/join.js b/server/src/commands/core/join.js index 965a8bc..644470e 100644 --- a/server/src/commands/core/join.js +++ b/server/src/commands/core/join.js @@ -6,7 +6,7 @@ const crypto = require('crypto'); const hash = (password) => { - let sha = crypto.createHash('sha256'); + const sha = crypto.createHash('sha256'); sha.update(password); return sha.digest('base64').substr(0, 6); }; @@ -15,15 +15,15 @@ const verifyNickname = (nick) => /^[a-zA-Z0-9_]{1,24}$/.test(nick); // exposed "login" function to allow hooks to verify user join events // returns object containing user info or string if error -exports.parseNickname = (core, data) => { - let userInfo = { +export function parseNickname(core, data) { + const userInfo = { nick: '', uType: 'user', trip: null, }; // seperate nick from password - let nickArray = data.nick.split('#', 2); + const nickArray = data.nick.split('#', 2); userInfo.nick = nickArray[0].trim(); if (!verifyNickname(userInfo.nick)) { @@ -31,90 +31,92 @@ exports.parseNickname = (core, data) => { return 'Nickname must consist of up to 24 letters, numbers, and underscores'; } - let password = nickArray[1]; + const password = nickArray[1]; if (hash(password + core.config.tripSalt) === core.config.adminTrip) { userInfo.uType = 'admin'; userInfo.trip = 'Admin'; - } else if (userInfo.nick.toLowerCase() == core.config.adminName.toLowerCase()) { // they've got the main-admin name while not being an admin + } else if (userInfo.nick.toLowerCase() === core.config.adminName.toLowerCase()) { + // they've got the main-admin name while not being an admin return 'You are not the admin, liar!'; } else if (password) { userInfo.trip = hash(password + core.config.tripSalt); } // TODO: disallow moderator impersonation - for (let mod of core.config.mods) { + // for (const mod of core.config.mods) { + core.config.mods.forEach((mod) => { if (userInfo.trip === mod.trip) { userInfo.uType = 'mod'; } - } + }); return userInfo; -}; +} // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // check for spam - if (server.police.frisk(socket.remoteAddress, 3)) { + if (server.police.frisk(socket.address, 3)) { return server.reply({ cmd: 'warn', - text: 'You are joining channels too fast. Wait a moment and try again.' + text: 'You are joining channels too fast. Wait a moment and try again.', }, socket); } // calling socket already in a channel if (typeof socket.channel !== 'undefined') { - return; + return true; } // check user input if (typeof data.channel !== 'string' || typeof data.nick !== 'string') { - return; + return true; } - let channel = data.channel.trim(); + const channel = data.channel.trim(); if (!channel) { // must join a non-blank channel - return; + return true; } - let userInfo = this.parseNickname(core, data); + const userInfo = this.parseNickname(core, data); if (typeof userInfo === 'string') { return server.reply({ cmd: 'warn', - text: userInfo + text: userInfo, }, socket); } // check if the nickname already exists in the channel - let userExists = server.findSockets({ + const userExists = server.findSockets({ channel: data.channel, - nick: (targetNick) => targetNick.toLowerCase() === userInfo.nick.toLowerCase() + nick: (targetNick) => targetNick.toLowerCase() === userInfo.nick.toLowerCase(), }); if (userExists.length > 0) { // that nickname is already in that channel return server.reply({ cmd: 'warn', - text: 'Nickname taken' + text: 'Nickname taken', }, socket); } userInfo.userHash = server.getSocketHash(socket); // prepare to notify channel peers - let newPeerList = server.findSockets({ channel: data.channel }); - let nicks = []; + const newPeerList = server.findSockets({ channel: data.channel }); + const nicks = []; - let joinAnnouncement = { + const joinAnnouncement = { cmd: 'onlineAdd', nick: userInfo.nick, trip: userInfo.trip || 'null', - hash: userInfo.userHash + hash: userInfo.userHash, }; // send join announcement and prep online set - for (let i = 0, l = newPeerList.length; i < l; i++) { + for (let i = 0, l = newPeerList.length; i < l; i += 1) { server.reply(joinAnnouncement, newPeerList[i]); nicks.push(newPeerList[i].nick); } @@ -131,18 +133,19 @@ exports.run = async (core, server, socket, data) => { // reply with channel peer list server.reply({ cmd: 'onlineSet', - nicks: nicks + nicks, }, socket); // stats are fun core.stats.increment('users-joined'); -}; -// module meta -exports.requiredData = ['channel', 'nick']; -exports.info = { + return true; +} + +export const requiredData = ['channel', 'nick']; +export const info = { name: 'join', description: 'Place calling socket into target channel with target nick & broadcast event to channel', usage: ` - API: { cmd: 'join', nick: '<your nickname>', channel: '<target channel>' }` + API: { cmd: 'join', nick: '<your nickname>', channel: '<target channel>' }`, }; diff --git a/server/src/commands/core/morestats.js b/server/src/commands/core/morestats.js index a71729c..3cbf8f3 100644 --- a/server/src/commands/core/morestats.js +++ b/server/src/commands/core/morestats.js @@ -3,37 +3,38 @@ */ // module support functions -const stripIndents = require('common-tags').stripIndents; +const { stripIndents } = require('common-tags'); const formatTime = (time) => { let seconds = time[0] + time[1] / 1e9; let minutes = Math.floor(seconds / 60); - seconds = seconds % 60; + seconds %= 60; let hours = Math.floor(minutes / 60); - minutes = minutes % 60; + minutes %= 60; - let days = Math.floor(hours / 24); - hours = hours % 24; + const days = Math.floor(hours / 24); + hours %= 24; return `${days.toFixed(0)}d ${hours.toFixed(0)}h ${minutes.toFixed(0)}m ${seconds.toFixed(0)}s`; }; // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket) { // gather connection and channel count let ips = {}; let channels = {}; - for (let client of server.clients) { + // for (const client of server.clients) { + this.clients.forEach((client) => { if (client.channel) { channels[client.channel] = true; - ips[client.remoteAddress] = true; + ips[client.address] = true; } - } + }); - let uniqueClientCount = Object.keys(ips).length; - let uniqueChannels = Object.keys(channels).length; + const uniqueClientCount = Object.keys(ips).length; + const uniqueChannels = Object.keys(channels).length; ips = null; channels = null; @@ -49,40 +50,39 @@ exports.run = async (core, server, socket, data) => { users-banned: ${(core.stats.get('users-banned') || 0)} users-kicked: ${(core.stats.get('users-kicked') || 0)} stats-requested: ${(core.stats.get('stats-requested') || 0)} - server-uptime: ${formatTime(process.hrtime(core.stats.get('start-time')))}` + server-uptime: ${formatTime(process.hrtime(core.stats.get('start-time')))}`, }, socket); // stats are fun core.stats.increment('stats-requested'); -}; +} // module hook functions -exports.initHooks = (server) => { +export function initHooks(server) { server.registerHook('in', 'chat', this.statsCheck, 26); -}; +} // hooks chat commands checking for /stats -exports.statsCheck = (core, server, socket, payload) => { +export function statsCheck(core, server, socket, payload) { if (typeof payload.text !== 'string') { return false; } if (payload.text.startsWith('/stats')) { this.run(core, server, socket, { - cmd: 'morestats' + cmd: 'morestats', }); return false; } return payload; -}; +} -// module meta -exports.info = { +export const info = { name: 'morestats', description: 'Sends back current server stats to the calling client', usage: ` API: { cmd: 'morestats' } - Text: /stats` + Text: /stats`, }; diff --git a/server/src/commands/core/move.js b/server/src/commands/core/move.js index 8e97a06..7eda88c 100644 --- a/server/src/commands/core/move.js +++ b/server/src/commands/core/move.js @@ -3,67 +3,67 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // check for spam - if (server.police.frisk(socket.remoteAddress, 6)) { + if (server.police.frisk(socket.address, 6)) { return server.reply({ cmd: 'warn', - text: 'You are changing channels too fast. Wait a moment before trying again.' + text: 'You are changing channels too fast. Wait a moment before trying again.', }, socket); } // check user input if (typeof data.channel !== 'string') { - return; + return true; } if (data.channel === socket.channel) { // they are trying to rejoin the channel - return; + return true; } // check that the nickname isn't already in target channel const currentNick = socket.nick.toLowerCase(); - let userExists = server.findSockets({ + const userExists = server.findSockets({ channel: data.channel, - nick: (targetNick) => targetNick.toLowerCase() === currentNick + nick: (targetNick) => targetNick.toLowerCase() === currentNick, }); if (userExists.length > 0) { // That nickname is already in that channel - return; + return true; } // broadcast leave notice to peers - let peerList = server.findSockets({ channel: socket.channel }); + const peerList = server.findSockets({ channel: socket.channel }); if (peerList.length > 1) { - for (let i = 0, l = peerList.length; i < l; i++) { + for (let i = 0, l = peerList.length; i < l; i += 1) { server.reply({ cmd: 'onlineRemove', - nick: peerList[i].nick + nick: peerList[i].nick, }, socket); - if (socket.nick !== peerList[i].nick){ + if (socket.nick !== peerList[i].nick) { server.reply({ cmd: 'onlineRemove', - nick: socket.nick + nick: socket.nick, }, peerList[i]); } } } // broadcast join notice to new peers - let newPeerList = server.findSockets({ channel: data.channel }); - let moveAnnouncement = { + const newPeerList = server.findSockets({ channel: data.channel }); + const moveAnnouncement = { cmd: 'onlineAdd', nick: socket.nick, trip: socket.trip || 'null', - hash: socket.hash + hash: socket.hash, }; - let nicks = []; + const nicks = []; - for (let i = 0, l = newPeerList.length; i < l; i++) { + for (let i = 0, l = newPeerList.length; i < l; i += 1) { server.reply(moveAnnouncement, newPeerList[i]); nicks.push(newPeerList[i].nick); } @@ -73,18 +73,19 @@ exports.run = async (core, server, socket, data) => { // reply with new user list server.reply({ cmd: 'onlineSet', - nicks: nicks + nicks, }, socket); // commit change socket.channel = data.channel; -}; -// module meta -exports.requiredData = ['channel']; -exports.info = { + return true; +} + +export const requiredData = ['channel']; +export const info = { name: 'move', description: 'This will change your current channel to the new one provided', usage: ` - API: { cmd: 'move', channel: '<target channel>' }` + API: { cmd: 'move', channel: '<target channel>' }`, }; diff --git a/server/src/commands/core/ping.js b/server/src/commands/core/ping.js index 1e710e5..7d8623d 100644 --- a/server/src/commands/core/ping.js +++ b/server/src/commands/core/ping.js @@ -3,12 +3,11 @@ */ // module main -exports.run = async (core, server, socket, data) => { - return; -}; +export async function run() { + +} -// module meta -exports.info = { +export const info = { name: 'ping', - description: 'This module is only in place to supress error notices legacy sources may get' + description: 'This module is only in place to supress error notices legacy sources may get', }; diff --git a/server/src/commands/core/stats.js b/server/src/commands/core/stats.js index ff4b1ef..8badd5b 100644 --- a/server/src/commands/core/stats.js +++ b/server/src/commands/core/stats.js @@ -3,19 +3,20 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket) { // gather connection and channel count let ips = {}; let channels = {}; - for (let client of server.clients) { + // for (const client of server.clients) { + this.clients.forEach((client) => { if (client.channel) { channels[client.channel] = true; - ips[client.remoteAddress] = true; + ips[client.address] = true; } - } + }); - let uniqueClientCount = Object.keys(ips).length; - let uniqueChannels = Object.keys(channels).length; + const uniqueClientCount = Object.keys(ips).length; + const uniqueChannels = Object.keys(channels).length; ips = null; channels = null; @@ -23,17 +24,16 @@ exports.run = async (core, server, socket, data) => { // dispatch info server.reply({ cmd: 'info', - text: `${uniqueClientCount} unique IPs in ${uniqueChannels} channels` + text: `${uniqueClientCount} unique IPs in ${uniqueChannels} channels`, }, socket); // stats are fun core.stats.increment('stats-requested'); -}; +} -// module meta -exports.info = { +export const info = { name: 'stats', description: 'Sends back legacy server stats to the calling client', usage: ` - API: { cmd: 'stats' }` + API: { cmd: 'stats' }`, }; diff --git a/server/src/commands/core/whisper.js b/server/src/commands/core/whisper.js index 109889d..1cfa157 100644 --- a/server/src/commands/core/whisper.js +++ b/server/src/commands/core/whisper.js @@ -11,36 +11,38 @@ const parseText = (text) => { return false; } + let sanitizedText = text; + // strip newlines from beginning and end - text = text.replace(/^\s*\n|^\s+$|\n\s*$/g, ''); + sanitizedText = sanitizedText.replace(/^\s*\n|^\s+$|\n\s*$/g, ''); // replace 3+ newlines with just 2 newlines - text = text.replace(/\n{3,}/g, "\n\n"); + sanitizedText = sanitizedText.replace(/\n{3,}/g, '\n\n'); - return text; + return sanitizedText; }; // module main -exports.run = async (core, server, socket, payload) => { +export async function run(core, server, socket, payload) { // check user input - let text = parseText(payload.text); + const text = parseText(payload.text); if (!text) { // lets not send objects or empty text, yea? - return server.police.frisk(socket.remoteAddress, 13); + return server.police.frisk(socket.address, 13); } // check for spam - let score = text.length / 83 / 4; - if (server.police.frisk(socket.remoteAddress, score)) { + const score = text.length / 83 / 4; + if (server.police.frisk(socket.address, score)) { return server.reply({ cmd: 'warn', - text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.' + text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.', }, socket); } - let targetNick = payload.nick; + const targetNick = payload.nick; if (!verifyNickname(targetNick)) { - return; + return true; } // find target user @@ -49,18 +51,18 @@ exports.run = async (core, server, socket, payload) => { if (targetClient.length === 0) { return server.reply({ cmd: 'warn', - text: 'Could not find user in channel' + text: 'Could not find user in channel', }, socket); } - targetClient = targetClient[0]; + [targetClient] = targetClient; server.reply({ cmd: 'info', type: 'whisper', from: socket.nick, trip: socket.trip || 'null', - text: `${socket.nick} whispered: ${text}` + text: `${socket.nick} whispered: ${text}`, }, targetClient); targetClient.whisperReply = socket.nick; @@ -68,42 +70,44 @@ exports.run = async (core, server, socket, payload) => { server.reply({ cmd: 'info', type: 'whisper', - text: `You whispered to @${targetNick}: ${text}` + text: `You whispered to @${targetNick}: ${text}`, }, socket); -}; + + return true; +} // module hook functions -exports.initHooks = (server) => { +export function initHooks(server) { server.registerHook('in', 'chat', this.whisperCheck, 20); -}; +} // hooks chat commands checking for /whisper -exports.whisperCheck = (core, server, socket, payload) => { +export function whisperCheck(core, server, socket, payload) { if (typeof payload.text !== 'string') { return false; } if (payload.text.startsWith('/whisper')) { - let input = payload.text.split(' '); + const input = payload.text.split(' '); // If there is no nickname target parameter if (input[1] === undefined) { server.reply({ cmd: 'warn', - text: 'Refer to `/help whisper` for instructions on how to use this command.' + text: 'Refer to `/help whisper` for instructions on how to use this command.', }, socket); return false; } - let target = input[1].replace(/@/g, ''); + const target = input[1].replace(/@/g, ''); input.splice(0, 2); - let whisperText = input.join(' '); + const whisperText = input.join(' '); this.run(core, server, socket, { cmd: 'whisper', nick: target, - text: whisperText + text: whisperText, }); return false; @@ -113,35 +117,34 @@ exports.whisperCheck = (core, server, socket, payload) => { if (typeof socket.whisperReply === 'undefined') { server.reply({ cmd: 'warn', - text: 'Cannot reply to nobody' + text: 'Cannot reply to nobody', }, socket); return false; } - let input = payload.text.split(' '); + const input = payload.text.split(' '); input.splice(0, 1); - let whisperText = input.join(' '); + const whisperText = input.join(' '); this.run(core, server, socket, { cmd: 'whisper', nick: socket.whisperReply, - text: whisperText + text: whisperText, }); return false; } return payload; -}; +} -// module meta -exports.requiredData = ['nick', 'text']; -exports.info = { +export const requiredData = ['nick', 'text']; +export const info = { name: 'whisper', description: 'Display text on targets screen that only they can see', usage: ` API: { cmd: 'whisper', nick: '<target name>', text: '<text to whisper>' } Text: /whisper <target name> <text to whisper> - Alt Text: /r <text to whisper, this will auto reply to the last person who whispered to you>` + Alt Text: /r <text to whisper, this will auto reply to the last person who whispered to you>`, }; diff --git a/server/src/commands/internal/disconnect.js b/server/src/commands/internal/disconnect.js index 520f8cb..07a125e 100644 --- a/server/src/commands/internal/disconnect.js +++ b/server/src/commands/internal/disconnect.js @@ -4,28 +4,29 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { if (data.cmdKey !== server.cmdKey) { // internal command attempt by client, increase rate limit chance and ignore - return server.police.frisk(socket.remoteAddress, 20); + return server.police.frisk(socket.address, 20); } // send leave notice to client peers if (socket.channel) { server.broadcast({ cmd: 'onlineRemove', - nick: socket.nick + nick: socket.nick, }, { channel: socket.channel }); } // commit close just in case socket.terminate(); -}; -// module meta -exports.requiredData = ['cmdKey']; -exports.info = { + return true; +} + +export const requiredData = ['cmdKey']; +export const info = { name: 'disconnect', usage: 'Internal Use Only', - description: 'Internally used to relay `onlineRemove` event to clients' + description: 'Internally used to relay `onlineRemove` event to clients', }; diff --git a/server/src/commands/internal/legacylayer.js b/server/src/commands/internal/legacylayer.js index 50f5fd7..c0daa13 100644 --- a/server/src/commands/internal/legacylayer.js +++ b/server/src/commands/internal/legacylayer.js @@ -3,18 +3,19 @@ */ // module main -exports.run = async (core, server, socket, data) => { - return; -}; +export async function run(core, server, socket, data) { + /** + * @todo + */ +} // module hook functions -exports.initHooks = (server) => { +export function initHooks(server) { // module is only a placeholder - //server.registerHook('out', '', this.); -}; + // server.registerHook('out', '', this.); +} -// module meta -exports.info = { +export const info = { name: 'legacylayer', - description: 'This module adjusts outgoing data, making it compatible with legacy clients' + description: 'This module adjusts outgoing data, making it compatible with legacy clients', }; diff --git a/server/src/commands/internal/socketreply.js b/server/src/commands/internal/socketreply.js index 5dadaf6..1ba8df2 100644 --- a/server/src/commands/internal/socketreply.js +++ b/server/src/commands/internal/socketreply.js @@ -3,20 +3,21 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { if (data.cmdKey !== server.cmdKey) { // internal command attempt by client, increase rate limit chance and ignore - return server.police.frisk(socket.remoteAddress, 20); + return server.police.frisk(socket.address, 20); } // send warning to target socket server.reply({ cmd: 'warn', text: data.text }, socket); -}; -// module meta -exports.requiredData = ['cmdKey', 'text']; -exports.info = { + return true; +} + +export const requiredData = ['cmdKey', 'text']; +export const info = { name: 'socketreply', usage: 'Internal Use Only', - description: 'Internally used to relay warnings to clients' + description: 'Internally used to relay warnings to clients', }; diff --git a/server/src/commands/mod/ban.js b/server/src/commands/mod/ban.js index 9c8eb4f..dd5f01e 100644 --- a/server/src/commands/mod/ban.js +++ b/server/src/commands/mod/ban.js @@ -3,53 +3,53 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin or mod if (socket.uType === 'user') { - return server.police.frisk(socket.remoteAddress, 10); + return server.police.frisk(socket.address, 10); } // check user input if (typeof data.nick !== 'string') { - return; + return true; } // find target user - let targetNick = data.nick; + const targetNick = data.nick; let badClient = server.findSockets({ channel: socket.channel, nick: targetNick }); if (badClient.length === 0) { return server.reply({ cmd: 'warn', - text: 'Could not find user in channel' + text: 'Could not find user in channel', }, socket); } - badClient = badClient[0]; + [badClient] = badClient; // i guess banning mods or admins isn't the best idea? if (badClient.uType !== 'user') { return server.reply({ cmd: 'warn', - text: 'Cannot ban other mods, how rude' + text: 'Cannot ban other mods, how rude', }, socket); } // commit arrest record - server.police.arrest(badClient.remoteAddress, badClient.hash); + server.police.arrest(badClient.address, badClient.hash); console.log(`${socket.nick} [${socket.trip}] banned ${targetNick} in ${socket.channel}`); // notify normal users server.broadcast({ cmd: 'info', - text: `Banned ${targetNick}` + text: `Banned ${targetNick}`, }, { channel: socket.channel, uType: 'user' }); // notify mods server.broadcast({ cmd: 'info', - text: `${socket.nick} banned ${targetNick} in ${socket.channel}, userhash: ${badClient.hash}` + text: `${socket.nick} banned ${targetNick} in ${socket.channel}, userhash: ${badClient.hash}`, }, { uType: 'mod' }); // force connection closed @@ -57,13 +57,14 @@ exports.run = async (core, server, socket, data) => { // stats are fun core.stats.increment('users-banned'); -}; -// module meta -exports.requiredData = ['nick']; -exports.info = { + return true; +} + +export const requiredData = ['nick']; +export const info = { name: 'ban', description: 'Disconnects the target nickname in the same channel as calling socket & adds to ratelimiter', usage: ` - API: { cmd: 'ban', nick: '<target nickname>' }` + API: { cmd: 'ban', nick: '<target nickname>' }`, }; diff --git a/server/src/commands/mod/dumb.js b/server/src/commands/mod/dumb.js index d5e8fee..644bd4f 100644 --- a/server/src/commands/mod/dumb.js +++ b/server/src/commands/mod/dumb.js @@ -4,22 +4,22 @@ */ // module constructor -exports.init = (core) => { +export function init(core) { if (typeof core.muzzledHashes === 'undefined') { core.muzzledHashes = {}; } -}; +} // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin or mod if (socket.uType === 'user') { - return server.police.frisk(socket.remoteAddress, 10); + return server.police.frisk(socket.address, 10); } // check user input if (typeof data.nick !== 'string') { - return; + return true; } // find target user @@ -28,56 +28,58 @@ exports.run = async (core, server, socket, data) => { if (badClient.length === 0) { return server.reply({ cmd: 'warn', - text: 'Could not find user in channel' + text: 'Could not find user in channel', }, socket); } - badClient = badClient[0]; + [badClient] = badClient; // likely dont need this, muting mods and admins is fine if (badClient.uType !== 'user') { return server.reply({ cmd: 'warn', - text: 'This trick wont work on mods and admin' + text: 'This trick wont work on mods and admin', }, socket); } // store hash in mute list - let record = core.muzzledHashes[badClient.hash] = { - dumb: true - } + const record = core.muzzledHashes[badClient.hash] = { + dumb: true, + }; // store allies if needed - if(data.allies && Array.isArray(data.allies)){ - record.allies = data.allies; + if (data.allies && Array.isArray(data.allies)) { + record.allies = data.allies; } // notify mods server.broadcast({ cmd: 'info', - text: `${socket.nick} muzzled ${data.nick} in ${socket.channel}, userhash: ${badClient.hash}` + text: `${socket.nick} muzzled ${data.nick} in ${socket.channel}, userhash: ${badClient.hash}`, }, { uType: 'mod' }); -}; + + return true; +} // module hook functions -exports.initHooks = (server) => { +export function initHooks(server) { server.registerHook('in', 'chat', this.chatCheck, 25); server.registerHook('in', 'invite', this.inviteCheck, 25); // TODO: add whisper hook, need hook priorities todo finished first -}; +} // hook incoming chat commands, shadow-prevent chat if they are muzzled -exports.chatCheck = (core, server, socket, payload) => { +export function chatCheck(core, server, socket, payload) { if (typeof payload.text !== 'string') { return false; } - if(core.muzzledHashes[socket.hash]){ + if (core.muzzledHashes[socket.hash]) { // build fake chat payload - mutedPayload = { + const mutedPayload = { cmd: 'chat', nick: socket.nick, - text: payload.text + text: payload.text, }; if (socket.trip) { @@ -85,50 +87,59 @@ exports.chatCheck = (core, server, socket, payload) => { } // broadcast to any duplicate connections in channel - server.broadcast( mutedPayload, { channel: socket.channel, hash: socket.hash }); + server.broadcast(mutedPayload, { channel: socket.channel, hash: socket.hash }); // broadcast to allies, if any - if(core.muzzledHashes[socket.hash].allies){ - server.broadcast( mutedPayload, { channel: socket.channel, nick: core.muzzledHashes[socket.hash].allies }); + if (core.muzzledHashes[socket.hash].allies) { + server.broadcast( + mutedPayload, + { + channel: socket.channel, + nick: core.muzzledHashes[socket.hash].allies, + }, + ); } - // blanket "spam" protection, may expose the ratelimiting lines from `chat` and use that, TODO: one day #lazydev - server.police.frisk(socket.remoteAddress, 9); + /** + * Blanket "spam" protection. + * May expose the ratelimiting lines from `chat` and use that + * @todo one day #lazydev + */ + server.police.frisk(socket.address, 9); return false; } return payload; -}; +} // shadow-prevent all invites from muzzled users -exports.inviteCheck = (core, server, socket, payload) => { +export function inviteCheck(core, server, socket, payload) { if (typeof payload.nick !== 'string') { return false; } - if(core.muzzledHashes[socket.hash]){ + if (core.muzzledHashes[socket.hash]) { // generate common channel - let channel = Math.random().toString(36).substr(2, 8); + const channel = Math.random().toString(36).substr(2, 8); // send fake reply server.reply({ cmd: 'info', - text: `You invited ${payload.nick} to ?${channel}` + text: `You invited ${payload.nick} to ?${channel}`, }, socket); return false; } return payload; -}; +} -// module meta -exports.requiredData = ['nick']; -exports.info = { +export const requiredData = ['nick']; +export const info = { name: 'dumb', description: 'Globally shadow mute a connection. Optional allies array will see muted messages.', usage: ` - API: { cmd: 'dumb', nick: '<target nick>', allies: ['<optional nick array>', ...] }` + API: { cmd: 'dumb', nick: '<target nick>', allies: ['<optional nick array>', ...] }`, }; -exports.info.aliases = ['muzzle', 'mute']; +info.aliases = ['muzzle', 'mute']; diff --git a/server/src/commands/mod/kick.js b/server/src/commands/mod/kick.js index f3bc7ca..cb01d5c 100644 --- a/server/src/commands/mod/kick.js +++ b/server/src/commands/mod/kick.js @@ -3,37 +3,37 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin or mod if (socket.uType === 'user') { - return server.police.frisk(socket.remoteAddress, 10); + return server.police.frisk(socket.address, 10); } // check user input if (typeof data.nick !== 'string') { if (typeof data.nick !== 'object' && !Array.isArray(data.nick)) { - return; + return true; } } // find target user(s) - let badClients = server.findSockets({ channel: socket.channel, nick: data.nick }); + const badClients = server.findSockets({ channel: socket.channel, nick: data.nick }); if (badClients.length === 0) { return server.reply({ cmd: 'warn', - text: 'Could not find user(s) in channel' + text: 'Could not find user(s) in channel', }, socket); } // check if found targets are kickable, commit kick let newChannel = ''; - let kicked = []; - for (let i = 0, j = badClients.length; i < j; i++) { + const kicked = []; + for (let i = 0, j = badClients.length; i < j; i += 1) { if (badClients[i].uType !== 'user') { server.reply({ cmd: 'warn', - text: 'Cannot kick other mods, how rude' + text: 'Cannot kick other mods, how rude', }, socket); } else { newChannel = Math.random().toString(36).substr(2, 8); @@ -42,7 +42,7 @@ exports.run = async (core, server, socket, data) => { // inform mods with where they were sent server.broadcast({ cmd: 'info', - text: `${badClients[i].nick} was banished to ?${newChannel}` + text: `${badClients[i].nick} was banished to ?${newChannel}`, }, { channel: socket.channel, uType: 'mod' }); kicked.push(badClients[i].nick); @@ -51,32 +51,33 @@ exports.run = async (core, server, socket, data) => { } if (kicked.length === 0) { - return; + return true; } // broadcast client leave event - for (let i = 0, j = kicked.length; i < j; i++) { + for (let i = 0, j = kicked.length; i < j; i += 1) { server.broadcast({ cmd: 'onlineRemove', - nick: kicked[i] + nick: kicked[i], }, { channel: socket.channel }); } // publicly broadcast kick event server.broadcast({ cmd: 'info', - text: `Kicked ${kicked.join(', ')}` + text: `Kicked ${kicked.join(', ')}`, }, { channel: socket.channel, uType: 'user' }); // stats are fun core.stats.increment('users-kicked', kicked.length); -}; -// module meta -exports.requiredData = ['nick']; -exports.info = { + return true; +} + +export const requiredData = ['nick']; +export const info = { name: 'kick', description: 'Silently forces target client(s) into another channel. `nick` may be string or array of strings', usage: ` - API: { cmd: 'kick', nick: '<target nick>' }` + API: { cmd: 'kick', nick: '<target nick>' }`, }; diff --git a/server/src/commands/mod/moveuser.js b/server/src/commands/mod/moveuser.js index c7fc4bf..b55c207 100644 --- a/server/src/commands/mod/moveuser.js +++ b/server/src/commands/mod/moveuser.js @@ -3,79 +3,79 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin or mod if (socket.uType === 'user') { - return server.police.frisk(socket.remoteAddress, 10); + return server.police.frisk(socket.address, 10); } // check user input if (typeof data.nick !== 'string' || typeof data.channel !== 'string') { - return; + return true; } if (data.channel === socket.channel) { // moving them into the same channel? y u do this? - return; + return true; } - let badClients = server.findSockets({ channel: socket.channel, nick: data.nick }); + const badClients = server.findSockets({ channel: socket.channel, nick: data.nick }); if (badClients.length === 0) { return server.reply({ cmd: 'warn', - text: 'Could not find user in channel' + text: 'Could not find user in channel', }, socket); } - let badClient = badClients[0]; + const badClient = badClients[0]; if (badClient.uType !== 'user') { return server.reply({ cmd: 'warn', - text: 'Cannot move other mods, how rude' + text: 'Cannot move other mods, how rude', }, socket); } const currentNick = badClient.nick.toLowerCase(); - let userExists = server.findSockets({ + const userExists = server.findSockets({ channel: data.channel, - nick: (targetNick) => targetNick.toLowerCase() === currentNick + nick: (targetNick) => targetNick.toLowerCase() === currentNick, }); if (userExists.length > 0) { // That nickname is already in that channel - return; + return true; } - let peerList = server.findSockets({ channel: socket.channel }); + const peerList = server.findSockets({ channel: socket.channel }); if (peerList.length > 1) { - for (let i = 0, l = peerList.length; i < l; i++) { + for (let i = 0, l = peerList.length; i < l; i += 1) { server.reply({ cmd: 'onlineRemove', - nick: peerList[i].nick + nick: peerList[i].nick, }, badClient); - if (badClient.nick !== peerList[i].nick){ + if (badClient.nick !== peerList[i].nick) { server.reply({ cmd: 'onlineRemove', - nick: badClient.nick + nick: badClient.nick, }, peerList[i]); } } } - let newPeerList = server.findSockets({ channel: data.channel }); - let moveAnnouncement = { + const newPeerList = server.findSockets({ channel: data.channel }); + const moveAnnouncement = { cmd: 'onlineAdd', nick: badClient.nick, trip: badClient.trip || 'null', - hash: server.getSocketHash(badClient) + hash: server.getSocketHash(badClient), }; - let nicks = []; + const nicks = []; - for (let i = 0, l = newPeerList.length; i < l; i++) { + for (let i = 0, l = newPeerList.length; i < l; i += 1) { server.reply(moveAnnouncement, newPeerList[i]); nicks.push(newPeerList[i].nick); } @@ -84,22 +84,23 @@ exports.run = async (core, server, socket, data) => { server.reply({ cmd: 'onlineSet', - nicks: nicks + nicks, }, badClient); badClient.channel = data.channel; - server.broadcast( { + server.broadcast({ cmd: 'info', - text: `${badClient.nick} was moved into ?${data.channel}` + text: `${badClient.nick} was moved into ?${data.channel}`, }, { channel: data.channel }); -}; -// module meta -exports.requiredData = ['nick', 'channel']; -exports.info = { + return true; +} + +export const requiredData = ['nick', 'channel']; +export const info = { name: 'moveuser', description: 'This will move the target user nick into another channel', usage: ` - API: { cmd: 'moveuser', nick: '<target nick>', channel: '<new channel>' }` + API: { cmd: 'moveuser', nick: '<target nick>', channel: '<new channel>' }`, }; diff --git a/server/src/commands/mod/speak.js b/server/src/commands/mod/speak.js index 23fc4de..5514545 100644 --- a/server/src/commands/mod/speak.js +++ b/server/src/commands/mod/speak.js @@ -3,32 +3,32 @@ * Author: simple */ - // module constructor - exports.init = (core) => { - if (typeof core.muzzledHashes === 'undefined') { - core.muzzledHashes = {}; - } - }; +// module constructor +export function init(core) { + if (typeof core.muzzledHashes === 'undefined') { + core.muzzledHashes = {}; + } +} // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin or mod if (socket.uType === 'user') { - return server.police.frisk(socket.remoteAddress, 10); + return server.police.frisk(socket.address, 10); } // check user input if (typeof data.ip !== 'string' && typeof data.hash !== 'string') { return server.reply({ cmd: 'warn', - text: "hash:'targethash' or ip:'1.2.3.4' is required" + text: "hash:'targethash' or ip:'1.2.3.4' is required", }, socket); } // find target & remove mute status let target; if (typeof data.ip === 'string') { - target = getSocketHash(data.ip); + target = server.getSocketHash(data.ip); } else { target = data.hash; } @@ -38,15 +38,16 @@ exports.run = async (core, server, socket, data) => { // notify mods server.broadcast({ cmd: 'info', - text: `${socket.nick} unmuzzled : ${target}` + text: `${socket.nick} unmuzzled : ${target}`, }, { uType: 'mod' }); -}; -// module meta -exports.info = { + return true; +} + +export const info = { name: 'speak', description: 'Pardon a dumb user to be able to speak again', usage: ` - API: { cmd: 'speak', ip/hash: '<target ip or hash' }` + API: { cmd: 'speak', ip/hash: '<target ip or hash' }`, }; -exports.info.aliases = ['unmuzzle', 'unmute']; +info.aliases = ['unmuzzle', 'unmute']; diff --git a/server/src/commands/mod/unban.js b/server/src/commands/mod/unban.js index 6744d9d..0d1e469 100644 --- a/server/src/commands/mod/unban.js +++ b/server/src/commands/mod/unban.js @@ -3,22 +3,23 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket, data) { // increase rate limit chance and ignore if not admin or mod if (socket.uType === 'user') { - return server.police.frisk(socket.remoteAddress, 10); + return server.police.frisk(socket.address, 10); } // check user input if (typeof data.ip !== 'string' && typeof data.hash !== 'string') { return server.reply({ cmd: 'warn', - text: "hash:'targethash' or ip:'1.2.3.4' is required" + text: "hash:'targethash' or ip:'1.2.3.4' is required", }, socket); } // find target - let mode, target; + let mode; let + target; if (typeof data.ip === 'string') { mode = 'ip'; target = data.ip; @@ -39,23 +40,24 @@ exports.run = async (core, server, socket, data) => { // reply with success server.reply({ cmd: 'info', - text: `Unbanned ${target}` + text: `Unbanned ${target}`, }, socket); // notify mods server.broadcast({ cmd: 'info', - text: `${socket.nick} unbanned: ${target}` + text: `${socket.nick} unbanned: ${target}`, }, { uType: 'mod' }); // stats are fun core.stats.decrement('users-banned'); -}; -// module meta -exports.info = { + return true; +} + +export const info = { name: 'unban', description: 'Removes target ip from the ratelimiter', usage: ` - API: { cmd: 'unban', ip/hash: '<target ip or hash>' }` + API: { cmd: 'unban', ip/hash: '<target ip or hash>' }`, }; diff --git a/server/src/commands/mod/unbanall.js b/server/src/commands/mod/unbanall.js index c285b80..49eeee5 100644 --- a/server/src/commands/mod/unbanall.js +++ b/server/src/commands/mod/unbanall.js @@ -3,34 +3,35 @@ */ // module main -exports.run = async (core, server, socket, data) => { +export async function run(core, server, socket) { // increase rate limit chance and ignore if not admin or mod if (socket.uType === 'user') { - return server.police.frisk(socket.remoteAddress, 10); + return server.police.frisk(socket.address, 10); } // remove arrest records - server.police.records = {}; + server.police.clear(); console.log(`${socket.nick} [${socket.trip}] unbanned all`); // reply with success server.reply({ cmd: 'info', - text: `Unbanned all ip addresses` + text: 'Unbanned all ip addresses', }, socket); // notify mods server.broadcast({ cmd: 'info', - text: `${socket.nick} unbanned all ip addresses` + text: `${socket.nick} unbanned all ip addresses`, }, { uType: 'mod' }); -}; -// module meta -exports.info = { + return true; +} + +export const info = { name: 'unbanall', description: 'Clears all banned ip addresses', usage: ` - API: { cmd: 'unbanall' }` + API: { cmd: 'unbanall' }`, }; diff --git a/server/src/scripts/configLib/SetupWizard.js b/server/src/scripts/configLib/SetupWizard.js index bd6ef24..ac2f3a0 100644 --- a/server/src/scripts/configLib/SetupWizard.js +++ b/server/src/scripts/configLib/SetupWizard.js @@ -1,40 +1,36 @@ +import { + start as _start, + get, +} from 'prompt'; + /** * Server setup wizard, quick server setup and all that jazz. . . - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * + * @author Marzavec ( https://github.com/marzavec ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) */ - -const fse = require('fs-extra'); -const prompt = require('prompt'); -const path = require('path'); - class SetupWizard { /** * Create a `SetupWizard` instance for initializing the server's config.json - * * @param {Object} serverConfig reference to the server config class */ - constructor (serverConfig) { + constructor(serverConfig) { this.serverConfig = serverConfig; } /** * Roll a d20 and begin the wizarding process - * */ - async start () { + async start() { // load the current config to use as defaults, if available - let currentConfig = await this.serverConfig.load() || {}; + const currentConfig = await this.serverConfig.load() || {}; // auto generate the salt if not currrently created - currentConfig.tripSalt = currentConfig.tripSalt || - [...Array(Math.floor(Math.random()*1024)+1024)].map(i=>(~~(Math.random()*36)).toString(36)).join(''); + currentConfig.tripSalt = currentConfig.tripSalt + || [...Array(Math.floor(Math.random() * 1024) + 1024)].map(() => (~~(Math.random() * 36)).toString(36)).join(''); // load the setup questions & set their defaults - let questions = require('../setupSchema/Questions.js'); + const questions = require('../setupSchema/Questions.js'); questions.properties = this.setQuestionDefaults(questions.properties, currentConfig); // force password re-entry @@ -45,19 +41,18 @@ class SetupWizard { require('../setupSchema/Banner.js'); // let's start playing 20 questions - prompt.start(); - prompt.get(questions, (err, result) => this.finalize(err, result)); + _start(); + get(questions, (err, result) => this.finalize(err, result)); } /** * Compares the currently loaded config with the stock questions, adds a default * and required option to the question - * * @param {Object} questions the set of questions from /setupSchema * @param {Object} currentConfig the current server options */ - setQuestionDefaults (questions, currentConfig) { - Object.keys(questions).forEach(qName => { + setQuestionDefaults(questions, currentConfig) { + Object.keys(questions).forEach((qName) => { if (typeof currentConfig[qName] !== 'undefined') { questions[qName].default = currentConfig[qName]; questions[qName].required = false; @@ -76,7 +71,7 @@ class SetupWizard { * @param {Object} err any errors generated by Prompt * @param {Object} result the answers / new config setup */ - async finalize (err, result) { + async finalize(err, result) { // output errors and die if needed if (err) { console.error(err); @@ -87,7 +82,7 @@ class SetupWizard { if (typeof result.mods === 'undefined') { result.mods = []; } - + // If we should log errors with the err stack when they occur. // See: CommandManager.js if (typeof result.logErrDetailed === 'undefined') { @@ -110,4 +105,4 @@ class SetupWizard { } } -module.exports = SetupWizard; +export default SetupWizard; diff --git a/server/src/scripts/configure.js b/server/src/scripts/configure.js index d7f2bf2..d96e0df 100644 --- a/server/src/scripts/configure.js +++ b/server/src/scripts/configure.js @@ -1,21 +1,17 @@ /** * Server configuration script, to (re)configure server options - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * + * @author Marzavec ( https://github.com/marzavec ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) */ -'use strict'; - // import required classes -const path = require('path'); -const ConfigManager = require('../serverLib/ConfigManager'); -const SetupWizard = require('./configLib/SetupWizard'); +import { join } from 'path'; +import ConfigManager from '../serverLib/ConfigManager'; +import SetupWizard from './configLib/SetupWizard'; // import and initialize configManager & dependencies -const serverConfig = new ConfigManager(path.join(__dirname, '../..')); +const serverConfig = new ConfigManager(join(__dirname, '../..')); const setup = new SetupWizard(serverConfig); setup.start(); diff --git a/server/src/scripts/setupSchema/Banner.js b/server/src/scripts/setupSchema/Banner.js index 823f0fe..f13fde7 100644 --- a/server/src/scripts/setupSchema/Banner.js +++ b/server/src/scripts/setupSchema/Banner.js @@ -5,8 +5,8 @@ * */ -const stripIndents = require('common-tags').stripIndents; -const chalk = require('chalk'); +import { stripIndents } from 'common-tags'; +import chalk from 'chalk'; // gotta have that sexy console console.log(stripIndents` @@ -20,9 +20,9 @@ console.log(stripIndents` ${chalk.white('Note:')} ${chalk.green('npm/yarn run config')} will re-run this utility. You will now be asked for the following: - - ${chalk.magenta(' Salt')}, the salt for username trip - - ${chalk.magenta('Admin Name')}, the initial admin username - - ${chalk.magenta('Admin Pass')}, the initial admin password - - ${chalk.magenta(' Port')}, the port for the websocket + - ${chalk.magenta(' Salt')}, the salt for username trip + - ${chalk.magenta('Admin Name')}, the initial admin username + - ${chalk.magenta('Admin Pass')}, the initial admin password + - ${chalk.magenta(' Port')}, the port for the websocket \u200b `); diff --git a/server/src/scripts/setupSchema/Questions.js b/server/src/scripts/setupSchema/Questions.js index f84d32f..532ba67 100644 --- a/server/src/scripts/setupSchema/Questions.js +++ b/server/src/scripts/setupSchema/Questions.js @@ -15,10 +15,10 @@ const Questions = { type: 'string', hidden: true, replace: '*', - before: value => { + before: (value) => { salt = value; return value; - } + }, }, adminName: { @@ -26,7 +26,7 @@ const Questions = { pattern: /^"?[a-zA-Z0-9_]+"?$/, type: 'string', message: 'Nicks can only contain letters, numbers and underscores', - before: value => value.replace(/"/g, '') + before: (value) => value.replace(/"/g, ''), }, adminTrip: { @@ -35,21 +35,21 @@ const Questions = { replace: '*', description: 'Admin Password', message: 'You must enter or re-enter a password', - before: value => { + before: (value) => { const crypto = require('crypto'); - let sha = crypto.createHash('sha256'); + const sha = crypto.createHash('sha256'); sha.update(value + salt); return sha.digest('base64').substr(0, 6); - } + }, }, websocketPort: { type: 'integer', message: 'The port may only be a number!', description: 'Websocket Port', - default: '6060' - } - } -} + default: '6060', + }, + }, +}; module.exports = Questions; diff --git a/server/src/serverLib/CommandManager.js b/server/src/serverLib/CommandManager.js index 0c4f0aa..e715c6b 100644 --- a/server/src/serverLib/CommandManager.js +++ b/server/src/serverLib/CommandManager.js @@ -1,47 +1,71 @@ -/** - * Commands / protocol manager- loads, validates and handles command execution - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * - */ - -const path = require('path'); -const didYouMean = require('didyoumean2').default; +import { + basename, + join, + sep, + dirname, + relative, +} from 'path'; +import didYouMean from 'didyoumean2'; // default command modules path const CmdDir = 'src/commands'; +/** + * Commands / protocol manager- loads, validates and handles command execution + * @property {Array} commands - Array of currently loaded command modules + * @property {Array} categories - Array of command modules categories + * @author Marzavec ( https://github.com/marzavec ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) + */ class CommandManager { /** * Create a `CommandManager` instance for handling commands/protocol * * @param {Object} core Reference to the global core object */ - constructor (core) { + constructor(core) { + /** + * Stored reference to the core + * @type {CoreApp} + */ this.core = core; + + /** + * Command module storage + * @type {Array} + */ this.commands = []; + + /** + * Command module category names (based off directory or module meta) + * @type {Array} + */ this.categories = []; - if (!this.core.config.hasOwnProperty('logErrDetailed')) { + + /** + * Full path to config.json file + * @type {String} + */ + if (typeof this.core.config.logErrDetailed === 'undefined') { this.core.config.logErrDetailed = false; } } /** * (Re)initializes name spaces for commands and starts load routine - * + * @public * @return {String} Module errors or empty if none */ - loadCommands () { + loadCommands() { this.commands = []; this.categories = []; const commandImports = this.core.dynamicImports.getImport(CmdDir); let cmdErrors = ''; - Object.keys(commandImports).forEach(file => { - let command = commandImports[file]; - let name = path.basename(file); + Object.keys(commandImports).forEach((file) => { + const command = commandImports[file]; + const name = basename(file); cmdErrors += this.validateAndLoad(command, file, name); }); @@ -50,29 +74,28 @@ class CommandManager { /** * Checks the module after having been `require()`ed in and reports errors - * * @param {Object} command reference to the newly loaded object * @param {String} file file path to the module * @param {String} name command (`cmd`) name - * + * @private * @return {String} Module errors or empty if none */ - validateAndLoad (command, file, name) { - let error = this.validateCommand(command); + validateAndLoad(command, file, name) { + const error = this.validateCommand(command); if (error) { - let errText = `Failed to load '${name}': ${error}`; + const errText = `Failed to load command module '${name}': ${error}`; console.log(errText); return errText; } if (!command.category) { - let base = path.join(this.core.dynamicImports.base, 'commands'); + const base = join(this.core.dynamicImports.base, 'commands'); let category = 'Uncategorized'; - if (file.indexOf(path.sep) > -1) { - category = path.dirname(path.relative(base, file)) - .replace(new RegExp(path.sep.replace('\\', '\\\\'), 'g'), '/'); + if (file.indexOf(sep) > -1) { + category = dirname(relative(base, file)) + .replace(new RegExp(sep.replace('\\', '\\\\'), 'g'), '/'); } command.info.category = category; @@ -86,7 +109,7 @@ class CommandManager { try { command.init(this.core); } catch (err) { - let errText = `Failed to initialize '${name}': ${err}`; + const errText = `Failed to initialize '${name}': ${err}`; console.log(errText); return errText; } @@ -99,121 +122,109 @@ class CommandManager { /** * Checks the module after having been `require()`ed in and reports errors - * * @param {Object} object reference to the newly loaded object - * + * @private * @return {String} Module errors or null if none */ - validateCommand (object) { - if (typeof object !== 'object') - return 'command setup is invalid'; - - if (typeof object.run !== 'function') - return 'run function is missing'; - - if (typeof object.info !== 'object') - return 'info object is missing'; - - if (typeof object.info.name !== 'string') - return 'info object is missing a valid name field'; + validateCommand(object) { + if (typeof object !== 'object') { return 'command setup is invalid'; } + if (typeof object.run !== 'function') { return 'run function is missing'; } + if (typeof object.info !== 'object') { return 'info object is missing'; } + if (typeof object.info.name !== 'string') { return 'info object is missing a valid name field'; } return null; } /** * Pulls all command names from a passed `category` - * * @param {String} category [Optional] filter return results by this category - * + * @public * @return {Array} Array of command modules matching the category */ - all (category) { + all(category) { return !category ? this.commands : this.commands.filter( - c => c.info.category.toLowerCase() === category.toLowerCase() - ); + (c) => c.info.category.toLowerCase() === category.toLowerCase(), + ); } /** - * Pulls all category names - * - * @return {Array} Array of sub directories under CmdDir + * All category names + * @public + * @readonly + * @return {Array} Array of command category names */ - get categoriesList () { + get categoriesList() { return this.categories; } /** - * Pulls command by name or alia(s) - * + * Pulls command by name or alias * @param {String} name name or alias of command - * + * @public * @return {Object} Target command module object */ - get (name) { + get(name) { return this.findBy('name', name) || this.commands.find( - command => command.info.aliases instanceof Array && - command.info.aliases.indexOf(name) > -1 + (command) => command.info.aliases instanceof Array + && command.info.aliases.indexOf(name) > -1, ); } /** * Pulls command by arbitrary search of the `module.info` attribute - * * @param {String} key name or alias of command * @param {String} value name or alias of command - * + * @public * @return {Object} Target command module object */ - findBy (key, value) { - return this.commands.find(c => c.info[key] === value); + findBy(key, value) { + return this.commands.find((c) => c.info[key] === value); } /** * Runs `initHooks` function on any modules that utilize the event - * + * @private * @param {Object} server main server object */ - initCommandHooks (server) { - this.commands.filter(c => typeof c.initHooks !== 'undefined').forEach( - c => c.initHooks(server) - ); + initCommandHooks(server) { + this.commands.filter((c) => typeof c.initHooks !== 'undefined').forEach( + (c) => c.initHooks(server), + ); } /** * Finds and executes the requested command, or fails with semi-intelligent error - * * @param {Object} server main server reference * @param {Object} socket calling socket reference * @param {Object} data command structure passed by socket (client) - * + * @public * @return {*} Arbitrary module return data */ - handleCommand (server, socket, data) { + handleCommand(server, socket, data) { // Try to find command first - let command = this.get(data.cmd); + const command = this.get(data.cmd); if (command) { return this.execute(command, server, socket, data); - } else { - // Then fail with helpful (sorta) message - return this.handleFail(server, socket, data); } + + // Then fail with helpful (sorta) message + return this.handleFail(server, socket, data); } /** * Requested command failure handler, attempts to find command and reports back - * * @param {Object} server main server reference * @param {Object} socket calling socket reference * @param {Object} data command structure passed by socket (client) - * + * @private * @return {*} Arbitrary module return data */ - handleFail (server, socket, data) { - const maybe = didYouMean(data.cmd, this.all().map(c => c.info.name), { + handleFail(server, socket, data) { + const maybe = didYouMean(data.cmd, this.all().map((c) => c.info.name), { threshold: 5, - thresholdType: 'edit-distance' + thresholdType: 'edit-distance', }); if (maybe) { @@ -221,7 +232,7 @@ class CommandManager { return this.handleCommand(server, socket, { cmd: 'socketreply', cmdKey: server.cmdKey, - text: `Command not found, did you mean: \`${maybe}\`?` + text: `Command not found, did you mean: \`${maybe}\`?`, }); } @@ -229,39 +240,37 @@ class CommandManager { return this.handleCommand(server, socket, { cmd: 'socketreply', cmdKey: server.cmdKey, - text: 'Unknown command' + text: 'Unknown command', }); } /** * Attempt to execute the requested command, fail if err or bad params - * * @param {Object} command target command module * @param {Object} server main server reference * @param {Object} socket calling socket reference * @param {Object} data command structure passed by socket (client) - * + * @private * @return {*} Arbitrary module return data */ - async execute (command, server, socket, data) { + async execute(command, server, socket, data) { if (typeof command.requiredData !== 'undefined') { - let missing = []; - for (let i = 0, len = command.requiredData.length; i < len; i++) { - if (typeof data[command.requiredData[i]] === 'undefined') - missing.push(command.requiredData[i]); + const missing = []; + for (let i = 0, len = command.requiredData.length; i < len; i += 1) { + if (typeof data[command.requiredData[i]] === 'undefined') { missing.push(command.requiredData[i]); } } if (missing.length > 0) { console.log(`Failed to execute '${ - command.info.name - }': missing required ${missing.join(', ')}\n\n`); + command.info.name + }': missing required ${missing.join(', ')}\n\n`); this.handleCommand(server, socket, { cmd: 'socketreply', cmdKey: server.cmdKey, text: `Failed to execute '${ - command.info.name - }': missing required ${missing.join(', ')}\n\n` + command.info.name + }': missing required ${missing.join(', ')}\n\n`, }); return null; @@ -271,20 +280,20 @@ class CommandManager { try { return await command.run(this.core, server, socket, data); } catch (err) { - let errText = `Failed to execute '${command.info.name}': `; - + const errText = `Failed to execute '${command.info.name}': `; + // If we have more detail enabled, then we get the trace // if it isn't, or the property doesn't exist, then we'll get only the message if (this.core.config.logErrDetailed === true) { console.log(errText + err.stack); } else { - console.log(errText + err.toString()) + console.log(errText + err.toString()); } this.handleCommand(server, socket, { cmd: 'socketreply', cmdKey: server.cmdKey, - text: errText + err.toString() + text: errText + err.toString(), }); return null; @@ -292,4 +301,4 @@ class CommandManager { } } -module.exports = CommandManager; +export default CommandManager; diff --git a/server/src/serverLib/ConfigManager.js b/server/src/serverLib/ConfigManager.js index e29a0e7..bb414be 100644 --- a/server/src/serverLib/ConfigManager.js +++ b/server/src/serverLib/ConfigManager.js @@ -1,38 +1,47 @@ +import dateFormat from 'dateformat'; +import { + existsSync, + ensureFileSync, + readJsonSync, + copySync, + writeJSONSync, + removeSync, +} from 'fs-extra'; +import { resolve } from 'path'; + /** * Server configuration manager, handling loading, creation, parsing and saving * of the main config.json file - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * + * @property {String} base - Base path that all imports are required in from + * @author Marzavec ( https://github.com/marzavec ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) */ -const dateFormat = require('dateformat'); -const fse = require('fs-extra'); -const path = require('path'); - class ConfigManager { /** * Create a `ConfigManager` instance for managing application settings - * * @param {String} basePath executing directory name; __dirname */ - constructor (basePath = __dirname) { - this.configPath = path.resolve(basePath, 'config/config.json'); + constructor(basePath = __dirname) { + /** + * Full path to config.json file + * @type {String} + */ + this.configPath = resolve(basePath, 'config/config.json'); - if (!fse.existsSync(this.configPath)){ - fse.ensureFileSync(this.configPath); + if (!existsSync(this.configPath)) { + ensureFileSync(this.configPath); } } /** - * Loads config.json (main server config) into mem - * - * @return {Object || Boolean} False if the config.json could not be loaded + * Loads config.json (main server config) into memory + * @public + * @return {(JSON|Boolean)} False if the config.json could not be loaded */ - async load () { + async load() { try { - this.config = fse.readJsonSync(this.configPath); + this.config = readJsonSync(this.configPath); } catch (e) { return false; } @@ -42,12 +51,12 @@ class ConfigManager { /** * Creates backup of current config into configPath - * + * @private * @return {String} Backed up config.json path */ - async backup () { + backup() { const backupPath = `${this.configPath}.${dateFormat('dd-mm-yy-HH-MM-ss')}.bak`; - fse.copySync(this.configPath, backupPath); + copySync(this.configPath, backupPath); return backupPath; } @@ -55,18 +64,18 @@ class ConfigManager { /** * First makes a backup of the current `config.json`, then writes current config * to disk - * + * @public * @return {Boolean} False on failure */ - async save () { - const backupPath = await this.backup(); + save() { + const backupPath = this.backup(); try { - fse.writeJSONSync(this.configPath, this.config, { - // Indent with two spaces + writeJSONSync(this.configPath, this.config, { + // Indent with two spaces spaces: 2, }); - fse.removeSync(backupPath); + removeSync(backupPath); return true; } catch (err) { @@ -78,18 +87,17 @@ class ConfigManager { /** * Updates current config[`key`] with `value` then writes changes to disk - * * @param {*} key arbitrary configuration key * @param {*} value new value to change `key` to - * + * @public * @return {Boolean} False on failure */ - async set (key, value) { + set(key, value) { const realKey = `${key}`; this.config[realKey] = value; - return await this.save(); + return this.save(); } } -module.exports = ConfigManager; +export default ConfigManager; diff --git a/server/src/serverLib/CoreApp.js b/server/src/serverLib/CoreApp.js index 6bab090..8c2225d 100644 --- a/server/src/serverLib/CoreApp.js +++ b/server/src/serverLib/CoreApp.js @@ -1,30 +1,32 @@ -/** - * The core / global reference object - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * - */ - -const path = require('path'); -const { +import { join } from 'path'; +import { CommandManager, ConfigManager, ImportsManager, MainServer, - StatsManager -} = require('./'); + StatsManager, +} from '.'; +/** + * The core app builds all required classes and maintains a central + * reference point across the app + * @property {ConfigManager} configManager - Provides loading and saving of the server config + * @property {Object} config - The current json config object + * @property {ImportsManager} dynamicImports - Dynamic require interface allowing hot reloading + * @property {CommandManager} commands - Manages and executes command modules + * @property {StatsManager} stats - Stores and adjusts arbritary stat data + * @property {MainServer} server - Main websocket server reference + * @author Marzavec ( https://github.com/marzavec ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) + */ class CoreApp { /** - * Create the main core instance. - */ - constructor () { - - } - - async init () { + * Load config then initialize children + * @public + * @return {void} + */ + async init() { await this.buildConfigManager(); this.buildImportManager(); @@ -33,8 +35,14 @@ class CoreApp { this.buildMainServer(); } - async buildConfigManager () { - this.configManager = new ConfigManager(path.join(__dirname, '../..')); + /** + * Creates a new instance of the ConfigManager, loads and checks + * the server config + * @private + * @return {void} + */ + async buildConfigManager() { + this.configManager = new ConfigManager(join(__dirname, '../..')); this.config = await this.configManager.load(); if (this.config === false) { @@ -43,23 +51,43 @@ class CoreApp { } } - buildImportManager () { - this.dynamicImports = new ImportsManager(path.join(__dirname, '../..')); + /** + * Creates a new instance of the ImportsManager + * @private + * @return {void} + */ + buildImportManager() { + this.dynamicImports = new ImportsManager(join(__dirname, '../..')); } - buildCommandsManager () { + /** + * Creates a new instance of the CommandManager and loads the command modules + * @private + * @return {void} + */ + buildCommandsManager() { this.commands = new CommandManager(this); this.commands.loadCommands(); } - buildStatsManager () { + /** + * Creates a new instance of the StatsManager and sets the server start time + * @private + * @return {void} + */ + buildStatsManager() { this.stats = new StatsManager(this); this.stats.set('start-time', process.hrtime()); } - buildMainServer () { + /** + * Creates a new instance of the MainServer + * @private + * @return {void} + */ + buildMainServer() { this.server = new MainServer(this); } } -module.exports = CoreApp; +export { CoreApp }; diff --git a/server/src/serverLib/ImportsManager.js b/server/src/serverLib/ImportsManager.js index 8db7b68..ac1fc4c 100644 --- a/server/src/serverLib/ImportsManager.js +++ b/server/src/serverLib/ImportsManager.js @@ -1,69 +1,77 @@ +import { + resolve, + basename as _basename, + relative, +} from 'path'; +import RecursiveRead from 'readdir-recursive'; + /** * Import managment base, used to load commands/protocol and configuration objects - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * + * @property {String} base - Base path that all imports are required in from + * @author Marzavec ( https://github.com/marzavec ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) */ - -const read = require('readdir-recursive'); -const path = require('path'); - class ImportsManager { /** - * Create a `ImportsManager` instance for (re)loading classes and config - * + * Create an `ImportsManager` instance for (re)loading classes and config * @param {String} basePath executing directory name; default __dirname */ - constructor (basePath) { + constructor(basePath) { + /** + * Stored reference to the base directory path + * @type {String} + */ this.basePath = basePath; + + /** + * Data holder for imported modules + * @type {Object} + */ this.imports = {}; } /** * Pull base path that all imports are required in from - * + * @public * @type {String} readonly */ - get base () { + get base() { return this.basePath; } /** * Gather all js files from target directory, then verify and load - * - * @param {String} dirName The name of the dir to load, relative to the basePath. - * + * @param {String} dirName The name of the dir to load, relative to the basePath + * @private * @return {String} Load errors or empty if none */ - loadDir (dirName) { - const dir = path.resolve(this.basePath, dirName); + loadDir(dirName) { + const dir = resolve(this.basePath, dirName); let errorText = ''; try { - read.fileSync(dir).forEach(file => { - const basename = path.basename(file); + RecursiveRead.fileSync(dir).forEach((file) => { + const basename = _basename(file); if (basename.startsWith('_') || !basename.endsWith('.js')) return; let imported; try { imported = require(file); + + if (!this.imports[dirName]) { + this.imports[dirName] = {}; + } + + this.imports[dirName][file] = imported; } catch (e) { - let err = `Unable to load modules from ${dirName} (${path.relative(dir, file)})\n${e}`; + const err = `Unable to load modules from ${dirName} (${relative(dir, file)})\n${e}`; errorText += err; console.error(err); - return errorText; - } - - if (!this.imports[dirName]) { - this.imports[dirName] = {}; } - - this.imports[dirName][file] = imported; }); } catch (e) { - let err = `Unable to load modules from ${dirName}\n${e}`; + const err = `Unable to load modules from ${dirName}\n${e}`; errorText += err; console.error(err); return errorText; @@ -75,15 +83,13 @@ class ImportsManager { /** * Unlink references to each loaded module, pray to google that gc knows it's job, * then reinitialize this class to start the reload - * - * @param {Array} dirName The name of the dir to load, relative to the _base path. - * + * @public * @return {String} Load errors or empty if none */ - reloadDirCache () { + reloadDirCache() { let errorText = ''; - Object.keys(this.imports).forEach(dir => { + Object.keys(this.imports).forEach((dir) => { Object.keys(this.imports[dir]).forEach((mod) => { delete require.cache[require.resolve(mod)]; }); @@ -97,20 +103,19 @@ class ImportsManager { /** * Pull reference to imported modules that were imported from dirName, or * load required directory if not found - * * @param {String} dirName The name of the dir to load, relative to the _base path. - * + * @public * @return {Object} Object containing command module paths and structs */ - getImport (dirName) { - let imported = this.imports[dirName]; + getImport(dirName) { + const imported = this.imports[dirName]; if (!imported) { this.loadDir(dirName); } - return Object.assign({}, this.imports[dirName]); + return { ...this.imports[dirName] }; } } -module.exports = ImportsManager; +export default ImportsManager; diff --git a/server/src/serverLib/MainServer.js b/server/src/serverLib/MainServer.js index 8ef5129..35a0d3e 100644 --- a/server/src/serverLib/MainServer.js +++ b/server/src/serverLib/MainServer.js @@ -1,58 +1,96 @@ +import { + Server as WsServer, + OPEN as SocketReady, +} from 'ws'; +import { createHash } from 'crypto'; +import RateLimiter from './RateLimiter'; + +import { ServerConst } from '../utility/Constants'; + /** * Main websocket server handling communications and connection events - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * + * @property {RateLimiter} police - Main rate limit handler + * @property {String} cmdKey - Internal use command key + * @author Marzavec ( https://github.com/marzavec ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) */ - -const WsServer = require('ws').Server; -const SocketReady = require('ws').OPEN; -const Crypto = require('crypto'); -const RateLimiter = require('./RateLimiter'); -const PulseSpeed = 16000; // ping all clients every X ms -const IpSalt = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join(''); -const InternalCmdKey = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join(''); - class MainServer extends WsServer { /** - * Create a HackChat server instance. - * - * @param {Object} core Reference to the global core object - */ - constructor (core) { + * Create a HackChat server instance + * @param {CoreApp} core Reference to the global core object + */ + constructor(core) { super({ port: core.config.websocketPort }); + /** + * Stored reference to the core + * @type {CoreApp} + */ this.core = core; + + /** + * Command key used to verify internal commands + * @type {String} + */ + this.internalCmdKey = [...Array(Math.floor(Math.random() * 128) + 128)].map(() => (~~(Math.random() * 36)).toString(36)).join(''); + + /** + * Salt used to hash a clients ip + * @type {String} + */ + this.ipSalt = [...Array(Math.floor(Math.random() * 128) + 128)].map(() => (~~(Math.random() * 36)).toString(36)).join(''); + + /** + * Data store for command hooks + * @type {Object} + */ this.hooks = {}; + + /** + * Main rate limit tracker + * @type {RateLimiter} + */ this.police = new RateLimiter(); + + /** + * Black listed command names + * @type {Object} + */ this.cmdBlacklist = {}; + /** + * Stored info about the last server error + * @type {ErrorEvent} + */ + this.lastErr = null; + this.setupServer(); this.loadHooks(); } /** * Internal command key getter. Used to verify that internal only commands - * originate internally and not from a connected client. - * TODO: update to a structure that cannot be passed through json - * - * @type {String} readonly + * originate internally and not from a connected client + * @todo Update to a structure that cannot be passed through json + * @type {String} + * @public + * @readonly */ - get cmdKey () { - return InternalCmdKey; + get cmdKey() { + return this.internalCmdKey; } /** * Create ping interval and setup server event listeners - * + * @private + * @return {void} */ - setupServer () { - this.heartBeat = setInterval(() => this.beatHeart(), PulseSpeed); + setupServer() { + this.heartBeat = setInterval(() => this.beatHeart(), ServerConst.PulseSpeed); this.on('error', (err) => { - this.handleError('server', err); + this.handleError(err); }); this.on('connection', (socket, request) => { @@ -62,66 +100,71 @@ class MainServer extends WsServer { /** * Send empty `ping` frame to each client - * + * @private + * @return {void} */ - beatHeart () { - let targetSockets = this.findSockets({}); + beatHeart() { + const targetSockets = this.findSockets({}); if (targetSockets.length === 0) { return; } - for (let i = 0, l = targetSockets.length; i < l; i++) { + for (let i = 0, l = targetSockets.length; i < l; i += 1) { try { if (targetSockets[i].readyState === SocketReady) { targetSockets[i].ping(); } - } catch (e) { } + } catch (e) { /* yolo */ } } } /** * Bind listeners for the new socket created on connection to this class - * - * @param {Object} socket New socket object + * @param {ws#WebSocket} socket New socket object * @param {Object} request Initial headers of the new connection + * @private + * @return {void} */ - newConnection (socket, request) { - socket.remoteAddress = request.headers['x-forwarded-for'] || request.connection.remoteAddress; + newConnection(socket, request) { + const newSocket = socket; - socket.on('message', (data) => { + newSocket.address = request.headers['x-forwarded-for'] || request.connection.remoteAddress; + + newSocket.on('message', (data) => { this.handleData(socket, data); }); - socket.on('close', () => { + newSocket.on('close', () => { this.handleClose(socket); }); - socket.on('error', (err) => { - this.handleError(socket, err); + newSocket.on('error', (err) => { + this.handleError(err); }); } /** * Handle incoming messages from clients, parse and check command, then hand-off - * - * @param {Object} socket Calling socket object + * @param {ws#WebSocket} socket Calling socket object * @param {String} data Message sent from client + * @private + * @return {void} */ - handleData (socket, data) { + handleData(socket, data) { // Don't penalize yet, but check whether IP is rate-limited - if (this.police.frisk(socket.remoteAddress, 0)) { + if (this.police.frisk(socket.address, 0)) { this.core.commands.handleCommand(this, socket, { cmd: 'socketreply', cmdKey: this.cmdKey, - text: 'You are being rate-limited or blocked.' + text: 'You are being rate-limited or blocked.', }); return; } // Penalize here, but don't do anything about it - this.police.frisk(socket.remoteAddress, 1); + this.police.frisk(socket.address, 1); // Ignore ridiculously large packets if (data.length > 65536) { @@ -141,11 +184,11 @@ class MainServer extends WsServer { return; } - // TODO: make this more flexible - /* - * Issue #1: hard coded `cmd` check - * Issue #2: hard coded `cmd` value checks - */ + /** + * @todo make the following more flexible + * Issue #1: hard coded `cmd` check + * Issue #2: hard coded `cmd` value checks + */ if (typeof payload.cmd === 'undefined') { return; } @@ -161,7 +204,7 @@ class MainServer extends WsServer { if (typeof this.cmdBlacklist[payload.cmd] === 'function') { return; } - // End TODO // + // End @todo // // Execute `in` (incoming data) hooks and process results payload = this.executeHooks('in', socket, payload); @@ -171,11 +214,11 @@ class MainServer extends WsServer { this.core.commands.handleCommand(this, socket, { cmd: 'socketreply', cmdKey: this.cmdKey, - text: payload + text: payload, }); return; - } else if (payload === false) { + } if (payload === false) { // A hook requested this data be dropped return; } @@ -185,87 +228,107 @@ class MainServer extends WsServer { } /** - * Handle socket close from clients - * - * @param {Object} socket Closing socket object + * Pass socket close event to disconnection command module + * @param {ws#WebSocket} socket Closing socket object + * @private + * @return {void} */ - handleClose (socket) { + handleClose(socket) { this.core.commands.handleCommand(this, socket, { cmd: 'disconnect', - cmdKey: this.cmdKey + cmdKey: this.cmdKey, }); } /** * "Handle" server or socket errors - * - * @param {Object||String} socket Calling socket object, or 'server' - * @param {String} err The sad stuff + * @param {ErrorEvent} err The sad stuff + * @private + * @return {void} */ - handleError (socket, err) { + handleError(err) { + this.lastErr = err; console.log(`Server error: ${err}`); } /** * Send data payload to specific socket/client - * * @param {Object} payload Object to convert to json for transmission - * @param {Object} socket The target client + * @param {ws#WebSocket} socket The target client + * @example + * server.send({ + * cmd: 'info', + * text: 'Only targetSocket will see this' + * }, targetSocket); + * @public + * @return {void} */ - send (payload, socket) { + send(payload, socket) { + let outgoingPayload = payload; + // Add timestamp to command - payload.time = Date.now(); + outgoingPayload.time = Date.now(); // Execute `in` (incoming data) hooks and process results - payload = this.executeHooks('out', socket, payload); + outgoingPayload = this.executeHooks('out', socket, outgoingPayload); - if (typeof payload === 'string') { + if (typeof outgoingPayload === 'string') { // A hook malfunctioned, reply with error this.core.commands.handleCommand(this, socket, { cmd: 'socketreply', cmdKey: this.cmdKey, - text: payload + text: outgoingPayload, }); return; - } else if (payload === false) { + } if (outgoingPayload === false) { // A hook requested this data be dropped return; } try { if (socket.readyState === SocketReady) { - socket.send(JSON.stringify(payload)); + socket.send(JSON.stringify(outgoingPayload)); } - } catch (e) { } + } catch (e) { /* yolo */ } } /** * Overload function for `this.send()` - * * @param {Object} payload Object to convert to json for transmission - * @param {Object} socket The target client + * @param {ws#WebSocket} socket The target client + * @example + * server.reply({ + * cmd: 'info', + * text: 'Only targetSocket will see this' + * }, targetSocket); + * @public + * @return {void} */ - reply (payload, socket) { + reply(payload, socket) { this.send(payload, socket); } /** * Finds sockets/clients that meet the filter requirements, then passes the data to them - * * @param {Object} payload Object to convert to json for transmission * @param {Object} filter see `this.findSockets()` - * + * @example + * server.broadcast({ + * cmd: 'info', + * text: 'Everyone in "programming" will see this' + * }, { channel: 'programming' }); + * @public * @return {Boolean} False if no clients matched the filter, true if data sent */ - broadcast (payload, filter) { - let targetSockets = this.findSockets(filter); + broadcast(payload, filter) { + const targetSockets = this.findSockets(filter); if (targetSockets.length === 0) { return false; } - for (let i = 0, l = targetSockets.length; i < l; i++) { + for (let i = 0, l = targetSockets.length; i < l; i += 1) { this.send(payload, targetSockets[i]); } @@ -274,51 +337,54 @@ class MainServer extends WsServer { /** * Finds sockets/clients that meet the filter requirements, returns result as array - * * @param {Object} data Object to convert to json for transmission * @param {Object} filter The socket must of equal or greater attribs matching `filter` - * = {} // matches all - * = { channel: 'programming' } // matches any socket where (`socket.channel` === 'programming') - * = { channel: 'programming', nick: 'Marzavec' } // matches any socket where (`socket.channel` === 'programming' && `socket.nick` === 'Marzavec') - * + * @example + * // match all sockets: + * `filter` = {} + * // match any socket where socket.channel === 'programming' + * `filter` = { channel: 'programming' } + * // match any socket where + * // socket.channel === 'programming' && socket.nick === 'Marzavec' + * `filter` = { channel: 'programming', nick: 'Marzavec' } + * @public * @return {Array} Clients who matched the filter requirements */ - findSockets (filter) { - let filterAttribs = Object.keys(filter); - let reqCount = filterAttribs.length; + findSockets(filter) { + const filterAttribs = Object.keys(filter); + const reqCount = filterAttribs.length; let curMatch; - let matches = []; - for ( let socket of this.clients ) { + const matches = []; + this.clients.forEach((socket) => { + // for (const socket of this.clients) { curMatch = 0; - for (let i = 0; i < reqCount; i++) { + for (let i = 0; i < reqCount; i += 1) { if (typeof socket[filterAttribs[i]] !== 'undefined') { - switch(typeof filter[filterAttribs[i]]) { + switch (typeof filter[filterAttribs[i]]) { case 'object': { if (Array.isArray(filter[filterAttribs[i]])) { if (filter[filterAttribs[i]].indexOf(socket[filterAttribs[i]]) !== -1) { - curMatch++; - } - } else { - if (socket[filterAttribs[i]] === filter[filterAttribs[i]]) { - curMatch++; + curMatch += 1; } + } else if (socket[filterAttribs[i]] === filter[filterAttribs[i]]) { + curMatch += 1; } - break; + break; } case 'function': { if (filter[filterAttribs[i]](socket[filterAttribs[i]])) { - curMatch++; + curMatch += 1; } - break; + break; } default: { if (socket[filterAttribs[i]] === filter[filterAttribs[i]]) { - curMatch++; + curMatch += 1; } - break; + break; } } } @@ -327,7 +393,7 @@ class MainServer extends WsServer { if (curMatch === reqCount) { matches.push(socket); } - } + }); return matches; } @@ -335,18 +401,20 @@ class MainServer extends WsServer { /** * Hashes target socket's remote address using non-static variable length salt * encodes and shortens the output, returns that value - * - * @param {Object||String} target Either the target socket or ip as string - * + * @param {(ws#WebSocket|String)} target Either the target socket or ip as string + * @example + * let userHash = server.getSocketHash('1.2.3.4'); + * let userHash = server.getSocketHash(client); + * @public * @return {String} Hashed client connection string */ - getSocketHash (target) { - let sha = Crypto.createHash('sha256'); + getSocketHash(target) { + const sha = createHash('sha256'); if (typeof target === 'string') { - sha.update(target + IpSalt); + sha.update(target + this.ipSalt); } else { - sha.update(target.remoteAddress + IpSalt); + sha.update(target.address + this.ipSalt); } return sha.digest('base64').substr(0, 15); @@ -355,9 +423,10 @@ class MainServer extends WsServer { /** * (Re)loads all command module hooks, then sorts their order of operation by * priority, ascending (0 being highest priority) - * + * @public + * @return {void} */ - loadHooks () { + loadHooks() { // clear current hooks (if any) this.clearHooks(); // notify each module to register their hooks (if any) @@ -366,23 +435,23 @@ class MainServer extends WsServer { let curHooks = []; let hookObj = []; - if (typeof this.hooks['in'] !== 'undefined') { + if (typeof this.hooks.in !== 'undefined') { // start sorting, with incoming first - curHooks = [ ...this.hooks['in'].keys() ]; - for (let i = 0, j = curHooks.length; i < j; i++) { - hookObj = this.hooks['in'].get(curHooks[i]); - hookObj.sort( (h1, h2) => h1.priority - h2.priority ); - this.hooks['in'].set(hookObj); + curHooks = [...this.hooks.in.keys()]; + for (let i = 0, j = curHooks.length; i < j; i += 1) { + hookObj = this.hooks.in.get(curHooks[i]); + hookObj.sort((h1, h2) => h1.priority - h2.priority); + this.hooks.in.set(hookObj); } } - if (typeof this.hooks['out'] !== 'undefined') { + if (typeof this.hooks.out !== 'undefined') { // then outgoing - curHooks = [ ...this.hooks['out'].keys() ]; - for (let i = 0, j = curHooks.length; i < j; i++) { - hookObj = this.hooks['out'].get(curHooks[i]); - hookObj.sort( (h1, h2) => h1.priority - h2.priority ); - this.hooks['out'].set(hookObj); + curHooks = [...this.hooks.out.keys()]; + for (let i = 0, j = curHooks.length; i < j; i += 1) { + hookObj = this.hooks.out.get(curHooks[i]); + hookObj.sort((h1, h2) => h1.priority - h2.priority); + this.hooks.out.set(hookObj); } } } @@ -391,17 +460,19 @@ class MainServer extends WsServer { * Adds a target function to an array of hooks. Hooks are executed either before * processing user input (`in`) or before sending data back to the client (`out`) * and allows a module to modify each payload before moving forward - * * @param {String} type The type of event, typically `in` (incoming) or `out` (outgoing) * @param {String} command Should match the desired `cmd` attrib of the payload - * @param {Function} hookFunction Target function to execute, should accept `server`, `socket` and `payload` as parameters - * @param {Number} priority Execution priority, hooks with priority 1 will be executed before hooks with priority 200 for example + * @param {Function} hookFunction Target function to execute, should accept + * `server`, `socket` and `payload` as parameters + * @param {Number} priority Execution priority, hooks with priority 1 will be executed before + * hooks with priority 200 for example + * @example + * // Create hook to add "and stuff" to every chat line + * server.registerHook('in', 'chat', (server, socket, payload) => payload.text += ' and stuff'); + * @public + * @return {void} */ - registerHook (type, command, hookFunction, priority) { - if (typeof priority === 'undefined') { - priority = 25; - } - + registerHook(type, command, hookFunction, priority = 25) { if (typeof this.hooks[type] === 'undefined') { this.hooks[type] = new Map(); } @@ -412,7 +483,7 @@ class MainServer extends WsServer { this.hooks[type].get(command).push({ run: hookFunction, - priority: priority + priority, }); } @@ -422,25 +493,25 @@ class MainServer extends WsServer { * A payload (modified or not) that will continue through the data flow * A boolean false to indicate halting the data through flow * A string which indicates an error occured in executing the hook - * * @param {String} type The type of event, typically `in` (incoming) or `out` (outgoing) - * @param {Object} socket Either the target client or the client triggering the hook (depending on `type`) - * @param {Object} payload Either incoming data from client or outgoing data (depending on `type`) - * - * @return {Object || Boolean} + * @param {ws#WebSocket} socket Either target client or client (depends on `type`) + * @param {Object} payload Either incoming data from client or outgoing data (depends on `type`) + * @private + * @return {Object|Boolean} */ - executeHooks (type, socket, payload) { - let command = payload.cmd; + executeHooks(type, socket, payload) { + const command = payload.cmd; + let newPayload = payload; if (typeof this.hooks[type] !== 'undefined') { if (this.hooks[type].has(command)) { - let hooks = this.hooks[type].get(command); + const hooks = this.hooks[type].get(command); - for (let i = 0, j = hooks.length; i < j; i++) { + for (let i = 0, j = hooks.length; i < j; i += 1) { try { - payload = hooks[i].run(this.core, this, socket, payload); + newPayload = hooks[i].run(this.core, this, socket, newPayload); } catch (err) { - let errText = `Hook failure, '${type}', '${command}': `; + const errText = `Hook failure, '${type}', '${command}': `; if (this.core.config.logErrDetailed === true) { console.log(errText + err.stack); } else { @@ -450,23 +521,24 @@ class MainServer extends WsServer { } // A hook function may choose to return false to prevent all further processing - if (payload === false) { + if (newPayload === false) { return false; } } } } - return payload; + return newPayload; } /** * Wipe server hooks to make ready for module reload calls - * + * @public + * @return {void} */ - clearHooks () { + clearHooks() { this.hooks = {}; } } -module.exports = MainServer; +export default MainServer; diff --git a/server/src/serverLib/RateLimiter.js b/server/src/serverLib/RateLimiter.js index 43cf077..3f9bc27 100644 --- a/server/src/serverLib/RateLimiter.js +++ b/server/src/serverLib/RateLimiter.js @@ -1,39 +1,60 @@ +import { RateLimits } from '../utility/Constants'; + /** * Tracks frequency of occurances based on `id` (remote address), then allows or * denies command execution based on comparison with `threshold` - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * + * @property {Object} data - The current stats data + * @author Marzavec ( https://github.com/marzavec ) + * @author Andrew Belt ( https://github.com/AndrewBelt ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) */ - class RateLimiter { /** - * Create a ratelimiter instance. - */ - constructor () { + * Create a ratelimiter instance + */ + constructor() { + /** + * Data holder rate limit records + * @type {Object} + */ this.records = {}; - this.halflife = 30 * 1000; // milliseconds - this.threshold = 25; + + /** + * Time in milliseconds to decrement ratelimit weight + * @type {Number} + */ + this.halflife = RateLimits.halflife; + + /** + * Weight until ratelimited + * @type {Number} + */ + this.threshold = RateLimits.threshold; + + /** + * Stores the associated connection fingerprint with record id + * @type {Array} + */ this.hashes = []; } /** * Finds current score by `id` - * * @param {String} id target id / address - * + * @private * @return {Object} Object containing the record meta */ - search (id) { + search(id) { let record = this.records[id]; if (!record) { - record = this.records[id] = { + this.records[id] = { time: Date.now(), - score: 0 - } + score: 0, + }; + + record = this.records[id]; } return record; @@ -41,20 +62,22 @@ class RateLimiter { /** * Adjusts the current ratelimit score by `deltaScore` - * * @param {String} id target id / address * @param {Number} deltaScore amount to adjust current score by - * + * @example + * // Penalize by 1 and store if connection is ratelimited or not + * let isLimited = police.frisk(socket.address, 1); + * @public * @return {Boolean} True if record threshold has been exceeded */ - frisk (id, deltaScore) { - let record = this.search(id); + frisk(id, deltaScore) { + const record = this.search(id); if (record.arrested) { return true; } - record.score *= Math.pow(2, -(Date.now() - record.time ) / this.halflife); + record.score *= 2 ** -(Date.now() - record.time) / this.halflife; record.score += deltaScore; record.time = Date.now(); @@ -67,11 +90,16 @@ class RateLimiter { /** * Statically set server to no longer accept traffic from `id` - * * @param {String} id target id / address + * @example + * // Usage within a command module: + * let badClient = server.findSockets({ channel: socket.channel, nick: targetNick }); + * server.police.arrest(badClient[0].address, badClient[0].hash); + * @public + * @return {void} */ - arrest (id, hash) { - let record = this.search(id); + arrest(id, hash) { + const record = this.search(id); record.arrested = true; this.hashes[hash] = id; @@ -79,17 +107,32 @@ class RateLimiter { /** * Remove statically assigned limit from `id` - * * @param {String} id target id / address + * @example + * // Usage within a command module: + * server.police.pardon('targetHashOrIP'); + * @public + * @return {void} */ - pardon (id) { - if (typeof this.hashes[id] !== 'undefined') { - id = this.hashes[id]; + pardon(id) { + let targetId = id; + if (typeof this.hashes[targetId] !== 'undefined') { + targetId = this.hashes[targetId]; } - let record = this.search(id); + const record = this.search(targetId); record.arrested = false; } + + /** + * Clear all records + * @public + * @return {void} + */ + clear() { + this.records = {}; + this.hashes = []; + } } -module.exports = RateLimiter; +export default RateLimiter; diff --git a/server/src/serverLib/StatsManager.js b/server/src/serverLib/StatsManager.js index e472504..b6e4f97 100644 --- a/server/src/serverLib/StatsManager.js +++ b/server/src/serverLib/StatsManager.js @@ -1,60 +1,79 @@ /** * Simple generic stats collection script for events occurances (etc) - * - * Version: v2.0.0 - * Developer: Marzavec ( https://github.com/marzavec ) - * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) - * + * @property {Object} data - The current stats data + * @author Marzavec ( https://github.com/marzavec ) + * @version v2.0.0 + * @license WTFPL ( http://www.wtfpl.net/txt/copying/ ) */ - class StatsManager { /** - * Create a stats instance. - * + * Create a stats instance */ - constructor () { + constructor() { + /** + * Data holder for the stats class + * @type {Object} + */ this.data = {}; } /** * Retrieve value of arbitrary `key` reference - * * @param {String} key Reference to the arbitrary store name - * + * @example + * // Find previously set `start-time` + * stats.get('start-time'); + * @public * @return {*} Data referenced by `key` */ - get (key) { + get(key) { return this.data[key]; } /** * Set value of arbitrary `key` reference - * * @param {String} key Reference to the arbitrary store name * @param {Number} value New value for `key` + * @example + * // Set `start-time` + * stats.set('start-time', process.hrtime()); + * @public + * @return {void} */ - set (key, value) { + set(key, value) { this.data[key] = value; } /** * Increase value of arbitrary `key` reference, by 1 or `amount` - * * @param {String} key Reference to the arbitrary store name - * @param {Number} amount Value to increase `key` by, or 1 if omitted + * @param {?Number} [amount=1] Value to increase `key` by, or 1 if omitted + * @example + * // Increment by `amount` + * stats.increment('users', 6); + * // Increment by 1 + * stats.increment('users'); + * @public + * @return {void} */ - increment (key, amount) { - this.set(key, (this.get(key) || 0) + (amount || 1)); + increment(key, amount = 1) { + this.set(key, (this.get(key) || 0) + amount); } /** * Reduce value of arbitrary `key` reference, by 1 or `amount` - * * @param {String} key Reference to the arbitrary store name - * @param {Number} amount Value to decrease `key` by, or 1 if omitted + * @param {?Number} [amount=1] Value to decrease `key` by, or 1 if omitted + * @example + * // Decrement by `amount` + * stats.decrement('users', 6); + * // Decrement by 1 + * stats.decrement('users'); + * @public + * @return {void} */ - decrement (key, amount) { - this.set(key, (this.get(key) || 0) - (amount || 1)); + decrement(key, amount = 1) { + this.set(key, (this.get(key) || 0) - amount); } } diff --git a/server/src/serverLib/index.js b/server/src/serverLib/index.js index 4583de6..a820588 100644 --- a/server/src/serverLib/index.js +++ b/server/src/serverLib/index.js @@ -1,8 +1,6 @@ -module.exports = { - CommandManager: require('./CommandManager'), - ConfigManager: require('./ConfigManager'), - ImportsManager: require('./ImportsManager'), - MainServer: require('./MainServer'), - RateLimiter: require('./RateLimiter'), - StatsManager: require('./StatsManager') -}; +export const CommandManager = require('./CommandManager').default; +export const ConfigManager = require('./ConfigManager').default; +export const ImportsManager = require('./ImportsManager').default; +export const MainServer = require('./MainServer').default; +export const RateLimiter = require('./RateLimiter').default; +export const StatsManager = require('./StatsManager'); diff --git a/server/src/utility/Constants.js b/server/src/utility/Constants.js new file mode 100644 index 0000000..6836421 --- /dev/null +++ b/server/src/utility/Constants.js @@ -0,0 +1,19 @@ +/** + * Rate limit options + * @typedef {Object} RateLimits + * @property {number} halflife Time in milliseconds to decrement ratelimit weight + * @property {number} threshold Weight until ratelimited + */ +exports.RateLimits = { + halflife: 30 * 1000, + threshold: 25, +}; + +/** + * Websocket server options + * @typedef {Object} ServerConst + * @property {number} PulseSpeed Time in milliseconds to ping each client + */ +exports.ServerConst = { + PulseSpeed: 16 * 1000, +}; |