diff --git a/.eslintrc.js b/.eslintrc.js index e6a5c74..a9157bc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,36 +1,19 @@ module.exports = { - "env": { - "es2021": true, - "node": true + env: { + es2021: true, + node: true, }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 12, - "sourceType": "module" + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 12, + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + rules: { + indent: ['error', 'tab'], + 'linebreak-style': ['error', 'unix'], + quotes: ['error', 'single'], + semi: ['error', 'always'], }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "indent": [ - "error", - "tab" - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "double" - ], - "semi": [ - "error", - "always" - ] - } }; diff --git a/.github/workflows/autoBuildDev.yml b/.github/workflows/autoBuildDev.yml new file mode 100644 index 0000000..006fa2e --- /dev/null +++ b/.github/workflows/autoBuildDev.yml @@ -0,0 +1,31 @@ +--- +name: "dev Build" + +on: + push: + branches: + - "dev" + +jobs: + pre-release: + name: "dev Pre-Release - Auto" + runs-on: "ubuntu-latest" + + steps: + - uses: actions/checkout@v2 + - name: "Install dependencies" + run: | + npm install + + - name: "Build" + run: | + npm run build + + - uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "dev" + prerelease: true + title: "dev Pre-Release - Auto" + files: | + build/* \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..93d4d23 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{ "useTabs": true, "tabWidth": 1, "semi": true, "printWidth": 160, "singleQuote": true } diff --git a/README.md b/README.md index 28cb16e..81382ea 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -![image](https://user-images.githubusercontent.com/6373693/115112327-2b69b680-9fd9-11eb-8239-45b30219f705.png)
+[![dev Build](https://github.com/Inrixia/Floatplane-Downloader/actions/workflows/autoBuildDev.yml/badge.svg?branch=dev)](https://github.com/Inrixia/Floatplane-Downloader/actions/workflows/autoBuildDev.yml) + +![image](https://user-images.githubusercontent.com/6373693/115112327-2b69b680-9fd9-11eb-8239-45b30219f705.png)
**This project is unofficial and not in any way affiliated with LMG**
**Join our discord! [discord.gg/aNTyMME](https://discord.gg/aNTyMME)**
@@ -16,9 +18,10 @@ If you encounter any issues please **[create a issue](https://github.com/Inrixia
# Install Guide +Looking for **Docker**?
+Get the image on: **[hub.docker.com/r/inrix/floatplane-downloader](https://hub.docker.com/r/inrix/floatplane-downloader)** and see instructions at **[the wiki](https://github.com/Inrixia/Floatplane-Downloader/blob/master/wiki/docker.md)**! 1. Download the latest stable binary for your OS from **[Releases](https://github.com/Inrixia/Floatplane-PlexDownloader/releases)**
- Looking for **Docker**? Go check out the image and install instructions **[Here](https://hub.docker.com/r/inrix/floatplane-downloader)**! 2. Run the binary and follow the setup prompts.
@@ -29,12 +32,17 @@ If you encounter any issues please **[create a issue](https://github.com/Inrixia For more info on setting up plex & avalible settings check out the Wikis: -## Wikis: +# Wikis: * [Setting up Plex](https://github.com/Inrixia/Floatplane-PlexDownloader/blob/master/wiki/plex.md) * [Settings Info](https://github.com/Inrixia/Floatplane-Downloader/blob/master/wiki/settings.md) +* [Docker Info](https://github.com/Inrixia/Floatplane-Downloader/blob/master/wiki/docker.md) + +
**Note**: This is licenced under the GNU Affero General Public License v3.0. I am happy for you to use/modify/contribute to the source code as long as you provide a link back to here. +
+ ### Images: ![image](https://user-images.githubusercontent.com/6373693/115110440-8d252300-9fcf-11eb-92a0-a813fcfcc632.png) ![image](https://user-images.githubusercontent.com/6373693/115112389-69ff7100-9fd9-11eb-92e2-b83c3241627b.png) diff --git a/dockerREADME.md b/dockerREADME.md deleted file mode 100644 index 7438429..0000000 --- a/dockerREADME.md +++ /dev/null @@ -1,86 +0,0 @@ -# Floatplane Downloader - -**This project is unofficial and not in any way affiliated with LMG**
-**Join our discord! [discord.gg/aNTyMME](https://discord.gg/aNTyMME)** -
- -**Floatplane Downloader** Automagically downloads the latest videos from [Floatplane](https://floatplane.com) and optionally formats them to be viewed in [Plex](https://www.plex.tv/).
-
-Both downloading videos as they release and archiving the entire backlog is supported!
-This requires a **[Floatplane](http://floatplane.com)** subscription. -
- -If you like the project, and want to support me can to throw some bits at my [PayPal](https://www.paypal.com/donate?business=XZX2VLBCVA766¤cy_code=NZD) <3 - -### Tags: -- `:latest` - - Latest release version of the downloader -- `:beta` - - WIP version used for testing, may be completely broken... -
- -## Quickstart: -There is a interactive series of console prompts to help you setup the downloader and login. If you dont want to or cannot work with a interactive terminal please skip down to **Enviroment Variables** - - $ docker run -it \ - -v [path]:/fp/db \ - -v [path]:/fp/videos \ - -e headless="true" \ - -e runQuickstartPrompts=true \ - inrix/floatplane-downloader - -- **[path]** should be replaced with a directory on your machine to hold persistent data. -- Setting the Quickstart environment variable to true will create an interactive terminal to walk you through setting up the downloader. - -**After going through the Quickstart, run without quickstart prompts:** - - $ docker run \ - -v [path]:/fp/config \ - -v [path]:/fp/db \ - -v [path]:/fp/artwork \ - -v [path]:/fp/videos \ - -e headless="true" \ - inrix/floatplane-downloader -
- -## Environment Variables: -Setting environment variables allows you to pass in your login details, removing the need to use the quickstart prompts to login/setup the downloader. - -**For login:** - - $ docker run \ - -v [path]:/fp/db \ - -v [path]:/fp/artwork \ - -v [path]:/fp/videos \ - -e headless="true" \ - -e username="YourUsernameHere" \ - -e password="YourPasswordHere" \ - -e token="Your2FactorCodeHere" \ - inrix/floatplane-downloader - -**For login + plex:** - - $ docker run \ - -v [path]:/fp/db \ - -v [path]:/fp/artwork \ - -v [path]:/fp/videos \ - -e headless="true" - -e username="YourUsernameHere" \ - -e password="YourPasswordHere" \ - -e plexUsername="YourPlexUsernameHere" \ - -e plexPassword="YourPexPasswordHere2FactorCodeHereIfYouHaveOne" \ - inrix/floatplane-downloader - -You can also use enviroment variables to overwrite/set config values, though the config is persisted under db/config.json.
-To do this you must take the key for the setting in the settings.json and write it with the dots **.** replaced with underscores **_** you can see an example for the setting `floatplane.videoResolution` below: - -**For settings:** - - $ docker run \ - -v [path]:/fp/db \ - -v [path]:/fp/artwork \ - -v [path]:/fp/videos \ - -e headless="true" \ - -e floatplane_videoResolution="1080" \ - -e plex_token="ThisRemovesTheNeedForPassingUsername/Password" \ - inrix/floatplane-downloader diff --git a/latest.json b/latest.json deleted file mode 100644 index f8603f0..0000000 --- a/latest.json +++ /dev/null @@ -1 +0,0 @@ -{"version": "5.0.0", "beta": "5.0.0"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c90e795..db7a601 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,29 +9,32 @@ "version": "5.0.0", "dependencies": { "@ctrl/plex": "^1.5.3", - "@inrixia/db": "^1.7.0", - "@inrixia/helpers": "^1.15.1", + "@inrixia/db": "^1.7.3", + "@inrixia/helpers": "^1.20.2", "ffbinaries": "^1.1.4", - "floatplane": "^3.0.5", + "floatplane": "^3.1.8", "got": "^11.8.2", - "multi-progress-bars": "^3.2.3", + "html-to-text": "^8.0.0", + "multi-progress-bars": "^3.2.4", "process.argv": "^0.6.0", - "prompts": "^2.4.0", + "prompts": "^2.4.1", "sanitize-filename": "^1.6.3", "semver": "^7.3.5", "tough-cookie": "^4.0.0", "tough-cookie-file-store": "^2.0.2" }, "devDependencies": { + "@types/html-to-text": "^8.0.0", "@types/multi-progress": "^2.0.3", - "@types/prompts": "^2.0.9", - "@types/semver": "^7.3.4", + "@types/prompts": "^2.0.13", + "@types/semver": "^7.3.6", "@types/tough-cookie-file-store": "^2.0.1", - "@typescript-eslint/eslint-plugin": "^4.18.0", - "@typescript-eslint/parser": "^4.18.0", - "eslint": "^7.22.0", - "pkg": "^4.4.9", - "typescript": "^4.2.3" + "@typescript-eslint/eslint-plugin": "^4.27.0", + "@typescript-eslint/parser": "^4.27.0", + "eslint": "^7.29.0", + "eslint-plugin-prettier": "^3.4.0", + "pkg": "^5.2.1", + "typescript": "^4.3.4" } }, "node_modules/@babel/code-frame": { @@ -44,20 +47,26 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/highlight/node_modules/ansi-styles": { @@ -101,6 +110,15 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -123,9 +141,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.12.tgz", - "integrity": "sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw==", + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", + "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -134,13 +152,15 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", - "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "node_modules/@babel/types": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.13.4" + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" } }, "node_modules/@ctrl/mac-address": { @@ -167,15 +187,15 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", @@ -186,47 +206,23 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "dependencies": { - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@inrixia/db": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@inrixia/db/-/db-1.7.0.tgz", - "integrity": "sha512-utzCYA+6wxwAB+p4vxl+yh2nm5pRurDfEsuFuc83WyRe+8gj2m/y5OQbsu2cc0Pz2cC48mCcPveOsrDeW9DrSA==" + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@inrixia/db/-/db-1.7.3.tgz", + "integrity": "sha512-lpdffUw/tW/oL+w5XF5ATpjZKE+SDPXvp3uuYSCe4J7mW0RXD7UogD3moWxc+E7D35Krb+7GSLVwzfGARbWF6A==" }, "node_modules/@inrixia/helpers": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@inrixia/helpers/-/helpers-1.15.1.tgz", - "integrity": "sha512-RS7c96KrfSN0Cp4MC/3ekxudtS6NyYVTIHRTP7lZEYBftNWYtaYtqYYyeO+YHQZ3CKN31EVyii6FUX1Mz72RgA==" + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@inrixia/helpers/-/helpers-1.20.2.tgz", + "integrity": "sha512-YmtcoduiSsBCOGyyhQ5hVzFnaOWj4kudg1QBrWSaYDzzV0Dz9EzVzWstCWtYLALj6US93DpumBPmQ1D9KCUd7Q==" }, "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" }, "engines": { @@ -234,31 +230,43 @@ } }, "node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", + "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { "node": ">= 8" } }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz", + "integrity": "sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA==", + "dependencies": { + "domhandler": "^4.2.0", + "selderee": "^0.6.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/@sindresorhus/is": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz", - "integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", + "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", "engines": { "node": ">=10" }, @@ -288,6 +296,12 @@ "@types/responselike": "*" } }, + "node_modules/@types/html-to-text": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-8.0.0.tgz", + "integrity": "sha512-sW+H3ByIvIPuZd599Id3nZAR3qc9jWf668V1aTEOTBmrYSAQsgXPNfZQpsLzPPh3gharrfEPq7ZbOhzkG57URA==", + "dev": true + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", @@ -318,9 +332,9 @@ } }, "node_modules/@types/node": { - "version": "14.14.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", - "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==" + "version": "15.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", + "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==" }, "node_modules/@types/progress": { "version": "2.0.3", @@ -332,9 +346,9 @@ } }, "node_modules/@types/prompts": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.10.tgz", - "integrity": "sha512-W3PEl3l4vmxdgfY6LUG7ysh+mLJOTOFYmSpiLe6MCo1OdEm8b5s6ZJfuTQgEpYNwcMiiaRzJespPS5Py2tqLlQ==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.13.tgz", + "integrity": "sha512-jwMOIGy49VruR/gYehhJYgpVzB+EVpEE7t7j9m1oTo4HMpOe7KmsyqdBuoxAzA5B4caUgx0cKrWr7wUEqMXJ7Q==", "dev": true, "dependencies": { "@types/node": "*" @@ -349,9 +363,9 @@ } }, "node_modules/@types/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==", + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.6.tgz", + "integrity": "sha512-0caWDWmpCp0uifxFh+FaqK3CuZ2SkRR/ZRxAV5+zNdC3QVUi6wyOJnefhPvtNt8NQWXB5OA93BUvZsXpWat2Xw==", "dev": true }, "node_modules/@types/tough-cookie": { @@ -370,19 +384,19 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz", - "integrity": "sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.27.0.tgz", + "integrity": "sha512-DsLqxeUfLVNp3AO7PC3JyaddmEHTtI9qTSAs+RB6ja27QvIM0TA8Cizn1qcS6vOu+WDLFJzkwkgweiyFhssDdQ==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "4.22.0", - "@typescript-eslint/scope-manager": "4.22.0", - "debug": "^4.1.1", + "@typescript-eslint/experimental-utils": "4.27.0", + "@typescript-eslint/scope-manager": "4.27.0", + "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "lodash": "^4.17.21", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "engines": { "node": "^10.12.0 || >=12.0.0" @@ -402,17 +416,17 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz", - "integrity": "sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.27.0.tgz", + "integrity": "sha512-n5NlbnmzT2MXlyT+Y0Jf0gsmAQzCnQSWXKy4RGSXVStjDvS5we9IWbh7qRVKdGcxT0WYlgcCYUK/HRg7xFhvjQ==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.22.0", - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/typescript-estree": "4.22.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.27.0", + "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/typescript-estree": "4.27.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" }, "engines": { "node": "^10.12.0 || >=12.0.0" @@ -426,15 +440,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.0.tgz", - "integrity": "sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.27.0.tgz", + "integrity": "sha512-XpbxL+M+gClmJcJ5kHnUpBGmlGdgNvy6cehgR6ufyxkEJMGP25tZKCaKyC0W/JVpuhU3VU1RBn7SYUPKSMqQvQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "4.22.0", - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/typescript-estree": "4.22.0", - "debug": "^4.1.1" + "@typescript-eslint/scope-manager": "4.27.0", + "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/typescript-estree": "4.27.0", + "debug": "^4.3.1" }, "engines": { "node": "^10.12.0 || >=12.0.0" @@ -453,13 +467,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz", - "integrity": "sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.27.0.tgz", + "integrity": "sha512-DY73jK6SEH6UDdzc6maF19AHQJBFVRf6fgAXHPXCGEmpqD4vYgPEzqpFz1lf/daSbOcMpPPj9tyXXDPW2XReAw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/visitor-keys": "4.22.0" + "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/visitor-keys": "4.27.0" }, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" @@ -470,9 +484,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz", - "integrity": "sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.27.0.tgz", + "integrity": "sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA==", "dev": true, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" @@ -483,18 +497,18 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz", - "integrity": "sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.27.0.tgz", + "integrity": "sha512-KH03GUsUj41sRLLEy2JHstnezgpS5VNhrJouRdmh6yNdQ+yl8w5LrSwBkExM+jWwCJa7Ct2c8yl8NdtNRyQO6g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/visitor-keys": "4.22.0", - "debug": "^4.1.1", - "globby": "^11.0.1", + "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/visitor-keys": "4.27.0", + "debug": "^4.3.1", + "globby": "^11.0.3", "is-glob": "^4.0.1", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "engines": { "node": "^10.12.0 || >=12.0.0" @@ -510,12 +524,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz", - "integrity": "sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.27.0.tgz", + "integrity": "sha512-es0GRYNZp0ieckZ938cEANfEhsfHrzuLrePukLKtY3/KPXcq1Xd555Mno9/GOgXhKzn0QfkDLVgqWO3dGY80bg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/types": "4.27.0", "eslint-visitor-keys": "^2.0.0" }, "engines": { @@ -552,6 +566,18 @@ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -689,6 +715,15 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -849,15 +884,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, - "node_modules/byline": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -867,35 +893,22 @@ } }, "node_modules/cacheable-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", - "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", + "normalize-url": "^6.0.1", "responselike": "^2.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", @@ -919,9 +932,9 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -952,6 +965,17 @@ "node": ">=6" } }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -996,6 +1020,11 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -1124,6 +1153,14 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -1170,6 +1207,11 @@ "node": ">=8" } }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1182,6 +1224,57 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", + "integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1259,23 +1352,43 @@ "node": ">=8.6" } }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "dependencies": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", + "estraverse": "^5.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1" }, @@ -1284,12 +1397,21 @@ "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=4.0" + "node": ">=6.0" }, "optionalDependencies": { "source-map": "~0.6.1" } }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/escodegen/node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -1342,28 +1464,30 @@ } }, "node_modules/eslint": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.24.0.tgz", - "integrity": "sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.29.0.tgz", + "integrity": "sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==", "dev": true, "dependencies": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", + "@eslint/eslintrc": "^0.4.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", + "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", @@ -1372,7 +1496,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -1381,7 +1505,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -1395,6 +1519,27 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-plugin-prettier": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz", + "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=5.0.0", + "prettier": ">=1.13.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -1409,6 +1554,33 @@ } }, "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", @@ -1423,7 +1595,7 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", @@ -1432,15 +1604,6 @@ "node": ">=4" } }, - "node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", @@ -1591,6 +1754,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", @@ -1696,13 +1865,14 @@ "dev": true }, "node_modules/floatplane": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/floatplane/-/floatplane-3.1.4.tgz", - "integrity": "sha512-7MtTy3p6QX+DicvGidFTJDZ6eAV1RGtudbCh4ZnB8xb1eWQdwzCo7HTIpjmOe39U0AV1Zmw8mlGopQVO8Lz4nQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/floatplane/-/floatplane-3.1.8.tgz", + "integrity": "sha512-1bYr2vgak5UqqibOtBQTPic6ykhfyA2kzi28L0k4SmS9FWz4BtlTfT0wW/E8wTigOSmDutMilTCkLN584If54A==", "dependencies": { - "got": "^11.6.0", + "got": "^11.8.2", "sails.io.js": "^1.2.1", - "socket.io-client": "2.1.1" + "socket.io-client": "2.1.1", + "tough-cookie": "^4.0.0" } }, "node_modules/forever-agent": { @@ -1836,18 +2006,13 @@ "node": ">=0.10.0" } }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-stream": { @@ -1879,9 +2044,9 @@ "dev": true }, "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1911,9 +2076,9 @@ } }, "node_modules/globals": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1938,9 +2103,9 @@ } }, "node_modules/globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "dependencies": { "array-union": "^2.1.0", @@ -2054,24 +2219,57 @@ "node": ">=8" } }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-to-text": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-8.0.0.tgz", + "integrity": "sha512-fEtul1OerF2aMEV+Wpy+Ue20tug134jOY1GIudtdqZi7D0uTudB2tVJBKfVhTL03dtqeJoF8gk8EPX9SyMEvLg==", + "dependencies": { + "@selderee/plugin-htmlparser2": "^0.6.0", + "deepmerge": "^4.2.2", + "he": "^1.2.0", + "htmlparser2": "^6.1.0", + "minimist": "^1.2.5", + "selderee": "^0.6.0" + }, + "bin": { + "html-to-text": "bin/cli.js" + }, + "engines": { + "node": ">=10.23.2" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", @@ -2103,6 +2301,19 @@ "node": ">=10.19.0" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -2192,40 +2403,28 @@ "dev": true }, "node_modules/into-stream": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-5.1.1.tgz", - "integrity": "sha512-krrAJ7McQxGGmvaYbB7Q1mcA+cRwg9Ij2RfWIeVesNBgVDZmzY/Fa4IpZUT3bmdRzMzdf/mzltCG2Dq99IZGBA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", "dev": true, "dependencies": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-boolean-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", - "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "node_modules/is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", "dev": true, "dependencies": { - "call-bind": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" + "has": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2269,30 +2468,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -2426,10 +2601,10 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "node_modules/lodash.truncate": { @@ -2480,19 +2655,19 @@ } }, "node_modules/mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", "dependencies": { - "mime-db": "1.47.0" + "mime-db": "1.48.0" }, "engines": { "node": ">= 0.6" @@ -2540,6 +2715,11 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, + "node_modules/moo": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", + "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2556,13 +2736,41 @@ } }, "node_modules/multistream": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/multistream/-/multistream-2.1.1.tgz", - "integrity": "sha512-xasv76hl6nr1dEy3lPvy7Ej7K/Lx3O/FCvwge8PeVJpciPPoNCbaANcNiBug3IpdvTveZUcAV0DJzdnUDMesNQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.1.0.tgz", + "integrity": "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "once": "^1.4.0", + "readable-stream": "^3.6.0" + } + }, + "node_modules/multistream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.5" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/napi-build-utils": { @@ -2577,10 +2785,31 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, "node_modules/node-abi": { - "version": "2.21.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.21.0.tgz", - "integrity": "sha512-smhrivuPqEM3H5LmnY3KU6HfYv0u4QklgAxfFyRNujKUzbUcYZ+Jc2EhukB9SRcD2VpqhxM7n/MIcp1Ua1/JMg==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.0.tgz", + "integrity": "sha512-g6bZh3YCKQRdwuO/tSZZYJAw622SjsRfJ2X0Iy4sSOHZ34/sPPdVBn8fev2tj7njzLwuqPw9uMtGsGkO5kIQvg==", "dev": true, "dependencies": { "semver": "^5.4.1" @@ -2595,6 +2824,15 @@ "semver": "bin/semver" } }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true, + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/noop-logger": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", @@ -2602,11 +2840,14 @@ "dev": true }, "node_modules/normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.0.1.tgz", + "integrity": "sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ==", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npmlog": { @@ -2653,9 +2894,9 @@ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" }, "node_modules/obop": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/obop/-/obop-0.2.0.tgz", - "integrity": "sha512-EpOqW06yOhfHJQrHioPW3nVOcGg2QaPbsxlP9QhxQnd9vpMpqXd1JZevF/8jPYmXhDklNxD49hQoyUANCZpuzA==" + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/obop/-/obop-0.2.1.tgz", + "integrity": "sha512-cw8GJUfhWqFK/NOEZfBUhn8mLrnyRfbWyK9MGl/hFaLQDh1QlaOapvnTsB8X+xWMFEi4pr5BCQXaiuDD2iFJww==" }, "node_modules/once": { "version": "1.4.0", @@ -2682,15 +2923,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-any": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-any/-/p-any-3.0.0.tgz", @@ -2707,9 +2939,9 @@ } }, "node_modules/p-cancelable": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", - "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "engines": { "node": ">=8" } @@ -2750,6 +2982,18 @@ "node": ">=6" } }, + "node_modules/parseley": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.7.0.tgz", + "integrity": "sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw==", + "dependencies": { + "moo": "^0.5.1", + "nearley": "^2.20.1" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", @@ -2785,9 +3029,9 @@ } }, "node_modules/path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "node_modules/path-type": { @@ -2810,9 +3054,9 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "node_modules/picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true, "engines": { "node": ">=8.6" @@ -2822,31 +3066,32 @@ } }, "node_modules/pkg": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/pkg/-/pkg-4.5.1.tgz", - "integrity": "sha512-UXKL88jGQ+FD4//PyrFeRcqurVQ3BVIfUNaEU9cXY24EJz08JyBj85qrGh0CFGvyzNb1jpwHOnns5Sw0M5H92Q==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.2.1.tgz", + "integrity": "sha512-kQ5Fla+76rdmFJNrEOgoklxAURl3uvhvX+m4LCQCRrI+q2lOgsx9pS02NhNuDVnyXsthluAuXCh5SKcMyw+cWw==", "dev": true, "dependencies": { - "@babel/parser": "7.13.12", - "@babel/runtime": "7.13.10", - "chalk": "^3.0.0", - "escodegen": "^1.14.1", - "fs-extra": "^8.1.0", - "globby": "^11.0.0", - "into-stream": "^5.1.1", + "@babel/parser": "7.13.13", + "@babel/types": "7.13.12", + "chalk": "^4.1.0", + "escodegen": "^2.0.0", + "fs-extra": "^9.1.0", + "globby": "^11.0.3", + "into-stream": "^6.0.0", "minimist": "^1.2.5", - "multistream": "^2.1.1", - "pkg-fetch": "2.6.9", + "multistream": "^4.1.0", + "pkg-fetch": "3.1.1", "prebuild-install": "6.0.1", "progress": "^2.0.3", - "resolve": "^1.15.1", - "stream-meter": "^1.0.4" + "resolve": "^1.20.0", + "stream-meter": "^1.0.4", + "tslib": "2.1.0" }, "bin": { "pkg": "lib-es5/bin.js" }, "peerDependencies": { - "node-notifier": ">=6.0.0" + "node-notifier": ">=9.0.1" }, "peerDependenciesMeta": { "node-notifier": { @@ -2855,60 +3100,93 @@ } }, "node_modules/pkg-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-2.6.9.tgz", - "integrity": "sha512-EnVR8LRILXBvaNP+wJOSY02c3+qDDfyEyR+aqAHLhcc9PBnbxFT9UZ1+If49goPQzQPn26TzF//fc6KXZ0aXEg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.1.1.tgz", + "integrity": "sha512-3GfpNwbwoTxge2TrVp6Oyz/FZJOoxF1r0+1YikOhnBXa2Di/VOJKtUObFHap76BFnyFo1fwh5vARWFR9TzLKUg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.9.2", - "byline": "^5.0.0", - "chalk": "^3.0.0", - "expand-template": "^2.0.3", - "fs-extra": "^8.1.0", - "minimist": "^1.2.5", + "chalk": "^4.1.0", + "fs-extra": "^9.1.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", "progress": "^2.0.3", - "request": "^2.88.0", - "request-progress": "^3.0.0", - "semver": "^6.3.0", - "unique-temp-dir": "^1.0.0" + "semver": "^7.3.5", + "yargs": "^16.2.0" }, "bin": { "pkg-fetch": "lib-es5/bin.js" } }, - "node_modules/pkg-fetch/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/pkg-fetch/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/pkg-fetch/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/pkg-fetch/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/pkg/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/pkg-fetch/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/pkg/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + } + }, + "node_modules/pkg/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/pkg/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" } }, "node_modules/prebuild-install": { @@ -2949,6 +3227,31 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", + "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -3044,6 +3347,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=" + }, + "node_modules/randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dependencies": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -3082,16 +3402,10 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, "engines": { "node": ">=8" @@ -3131,15 +3445,6 @@ "node": ">= 6" } }, - "node_modules/request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "dev": true, - "dependencies": { - "throttleit": "^1.0.0" - } - }, "node_modules/request/node_modules/tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -3152,6 +3457,15 @@ "node": ">=0.8" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3196,6 +3510,14 @@ "lowercase-keys": "^2.0.0" } }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "engines": { + "node": ">=0.12" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3272,6 +3594,17 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "node_modules/selderee": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.6.0.tgz", + "integrity": "sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg==", + "dependencies": { + "parseley": "^0.7.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -3567,29 +3900,26 @@ } }, "node_modules/table": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.1.0.tgz", - "integrity": "sha512-T4G5KMmqIk6X87gLKWyU5exPpTjLjY5KyrFWaIjv3SvgaIUGXV7UEzGEnZJdTA38/yUS6f9PlKezQ0bYXG3iIQ==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", "dev": true, "dependencies": { "ajv": "^8.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", "lodash.clonedeep": "^4.5.0", - "lodash.flatten": "^4.4.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10.0.0" } }, "node_modules/table/node_modules/ajv": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.1.0.tgz", - "integrity": "sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -3656,17 +3986,20 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "node_modules/throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, "node_modules/to-array": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3712,9 +4045,9 @@ } }, "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", "dev": true }, "node_modules/tsutils": { @@ -3732,6 +4065,12 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -3777,9 +4116,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "node_modules/typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz", + "integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3789,31 +4128,11 @@ "node": ">=4.2.0" } }, - "node_modules/uid2": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", - "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=", - "dev": true - }, "node_modules/ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" }, - "node_modules/unique-temp-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz", - "integrity": "sha1-bc6VsmgcoAPuv7MEpBX5y6vMU4U=", - "dev": true, - "dependencies": { - "mkdirp": "^0.5.1", - "os-tmpdir": "^1.0.1", - "uid2": "0.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -3844,6 +4163,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "bin": { "uuid": "bin/uuid" } @@ -3949,6 +4269,23 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3992,11 +4329,47 @@ "node": ">=0.4.0" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -4023,18 +4396,18 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -4074,6 +4447,12 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -4092,18 +4471,20 @@ } }, "@babel/parser": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.12.tgz", - "integrity": "sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw==", + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", + "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", "dev": true }, - "@babel/runtime": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", - "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "@babel/types": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { - "regenerator-runtime": "^0.13.4" + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" } }, "@ctrl/mac-address": { @@ -4124,79 +4505,71 @@ } }, "@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } } }, "@inrixia/db": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@inrixia/db/-/db-1.7.0.tgz", - "integrity": "sha512-utzCYA+6wxwAB+p4vxl+yh2nm5pRurDfEsuFuc83WyRe+8gj2m/y5OQbsu2cc0Pz2cC48mCcPveOsrDeW9DrSA==" + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@inrixia/db/-/db-1.7.3.tgz", + "integrity": "sha512-lpdffUw/tW/oL+w5XF5ATpjZKE+SDPXvp3uuYSCe4J7mW0RXD7UogD3moWxc+E7D35Krb+7GSLVwzfGARbWF6A==" }, "@inrixia/helpers": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@inrixia/helpers/-/helpers-1.15.1.tgz", - "integrity": "sha512-RS7c96KrfSN0Cp4MC/3ekxudtS6NyYVTIHRTP7lZEYBftNWYtaYtqYYyeO+YHQZ3CKN31EVyii6FUX1Mz72RgA==" + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@inrixia/helpers/-/helpers-1.20.2.tgz", + "integrity": "sha512-YmtcoduiSsBCOGyyhQ5hVzFnaOWj4kudg1QBrWSaYDzzV0Dz9EzVzWstCWtYLALj6US93DpumBPmQ1D9KCUd7Q==" }, "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", + "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, + "@selderee/plugin-htmlparser2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz", + "integrity": "sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA==", + "requires": { + "domhandler": "^4.2.0", + "selderee": "^0.6.0" + } + }, "@sindresorhus/is": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz", - "integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", + "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==" }, "@szmarczak/http-timer": { "version": "4.0.5", @@ -4217,6 +4590,12 @@ "@types/responselike": "*" } }, + "@types/html-to-text": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-8.0.0.tgz", + "integrity": "sha512-sW+H3ByIvIPuZd599Id3nZAR3qc9jWf668V1aTEOTBmrYSAQsgXPNfZQpsLzPPh3gharrfEPq7ZbOhzkG57URA==", + "dev": true + }, "@types/http-cache-semantics": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", @@ -4247,9 +4626,9 @@ } }, "@types/node": { - "version": "14.14.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", - "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==" + "version": "15.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", + "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==" }, "@types/progress": { "version": "2.0.3", @@ -4261,9 +4640,9 @@ } }, "@types/prompts": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.10.tgz", - "integrity": "sha512-W3PEl3l4vmxdgfY6LUG7ysh+mLJOTOFYmSpiLe6MCo1OdEm8b5s6ZJfuTQgEpYNwcMiiaRzJespPS5Py2tqLlQ==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.0.13.tgz", + "integrity": "sha512-jwMOIGy49VruR/gYehhJYgpVzB+EVpEE7t7j9m1oTo4HMpOe7KmsyqdBuoxAzA5B4caUgx0cKrWr7wUEqMXJ7Q==", "dev": true, "requires": { "@types/node": "*" @@ -4278,9 +4657,9 @@ } }, "@types/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==", + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.6.tgz", + "integrity": "sha512-0caWDWmpCp0uifxFh+FaqK3CuZ2SkRR/ZRxAV5+zNdC3QVUi6wyOJnefhPvtNt8NQWXB5OA93BUvZsXpWat2Xw==", "dev": true }, "@types/tough-cookie": { @@ -4299,85 +4678,85 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz", - "integrity": "sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.27.0.tgz", + "integrity": "sha512-DsLqxeUfLVNp3AO7PC3JyaddmEHTtI9qTSAs+RB6ja27QvIM0TA8Cizn1qcS6vOu+WDLFJzkwkgweiyFhssDdQ==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.22.0", - "@typescript-eslint/scope-manager": "4.22.0", - "debug": "^4.1.1", + "@typescript-eslint/experimental-utils": "4.27.0", + "@typescript-eslint/scope-manager": "4.27.0", + "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "lodash": "^4.17.21", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" } }, "@typescript-eslint/experimental-utils": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz", - "integrity": "sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.27.0.tgz", + "integrity": "sha512-n5NlbnmzT2MXlyT+Y0Jf0gsmAQzCnQSWXKy4RGSXVStjDvS5we9IWbh7qRVKdGcxT0WYlgcCYUK/HRg7xFhvjQ==", "dev": true, "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.22.0", - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/typescript-estree": "4.22.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.27.0", + "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/typescript-estree": "4.27.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.0.tgz", - "integrity": "sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.27.0.tgz", + "integrity": "sha512-XpbxL+M+gClmJcJ5kHnUpBGmlGdgNvy6cehgR6ufyxkEJMGP25tZKCaKyC0W/JVpuhU3VU1RBn7SYUPKSMqQvQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.22.0", - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/typescript-estree": "4.22.0", - "debug": "^4.1.1" + "@typescript-eslint/scope-manager": "4.27.0", + "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/typescript-estree": "4.27.0", + "debug": "^4.3.1" } }, "@typescript-eslint/scope-manager": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz", - "integrity": "sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.27.0.tgz", + "integrity": "sha512-DY73jK6SEH6UDdzc6maF19AHQJBFVRf6fgAXHPXCGEmpqD4vYgPEzqpFz1lf/daSbOcMpPPj9tyXXDPW2XReAw==", "dev": true, "requires": { - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/visitor-keys": "4.22.0" + "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/visitor-keys": "4.27.0" } }, "@typescript-eslint/types": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz", - "integrity": "sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.27.0.tgz", + "integrity": "sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz", - "integrity": "sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.27.0.tgz", + "integrity": "sha512-KH03GUsUj41sRLLEy2JHstnezgpS5VNhrJouRdmh6yNdQ+yl8w5LrSwBkExM+jWwCJa7Ct2c8yl8NdtNRyQO6g==", "dev": true, "requires": { - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/visitor-keys": "4.22.0", - "debug": "^4.1.1", - "globby": "^11.0.1", + "@typescript-eslint/types": "4.27.0", + "@typescript-eslint/visitor-keys": "4.27.0", + "debug": "^4.3.1", + "globby": "^11.0.3", "is-glob": "^4.0.1", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "semver": "^7.3.5", + "tsutils": "^3.21.0" } }, "@typescript-eslint/visitor-keys": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz", - "integrity": "sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.27.0.tgz", + "integrity": "sha512-es0GRYNZp0ieckZ938cEANfEhsfHrzuLrePukLKtY3/KPXcq1Xd555Mno9/GOgXhKzn0QfkDLVgqWO3dGY80bg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/types": "4.27.0", "eslint-visitor-keys": "^2.0.0" } }, @@ -4399,6 +4778,15 @@ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -4508,6 +4896,12 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -4624,41 +5018,25 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, - "byline": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", - "dev": true - }, "cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" }, "cacheable-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", - "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", "requires": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", + "normalize-url": "^6.0.1", "responselike": "^2.0.0" } }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", @@ -4676,9 +5054,9 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4700,6 +5078,17 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -4735,6 +5124,11 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -4833,6 +5227,11 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, "defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -4864,6 +5263,11 @@ "path-type": "^4.0.0" } }, + "discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=" + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4873,6 +5277,39 @@ "esutils": "^2.0.2" } }, + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" + }, + "domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", + "integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -4949,25 +5386,42 @@ "ansi-colors": "^4.1.1" } }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "requires": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", + "estraverse": "^5.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" }, "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -5010,28 +5464,30 @@ } }, "eslint": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.24.0.tgz", - "integrity": "sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.29.0.tgz", + "integrity": "sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", + "@eslint/eslintrc": "^0.4.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", + "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", @@ -5040,7 +5496,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -5049,9 +5505,37 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + } + } + }, + "eslint-plugin-prettier": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz", + "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" } }, "eslint-scope": { @@ -5065,26 +5549,18 @@ } }, "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "eslint-visitor-keys": "^2.0.0" } }, "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true }, "espree": { @@ -5205,6 +5681,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", @@ -5295,13 +5777,14 @@ "dev": true }, "floatplane": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/floatplane/-/floatplane-3.1.4.tgz", - "integrity": "sha512-7MtTy3p6QX+DicvGidFTJDZ6eAV1RGtudbCh4ZnB8xb1eWQdwzCo7HTIpjmOe39U0AV1Zmw8mlGopQVO8Lz4nQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/floatplane/-/floatplane-3.1.8.tgz", + "integrity": "sha512-1bYr2vgak5UqqibOtBQTPic6ykhfyA2kzi28L0k4SmS9FWz4BtlTfT0wW/E8wTigOSmDutMilTCkLN584If54A==", "requires": { - "got": "^11.6.0", + "got": "^11.8.2", "sails.io.js": "^1.2.1", - "socket.io-client": "2.1.1" + "socket.io-client": "2.1.1", + "tough-cookie": "^4.0.0" } }, "forever-agent": { @@ -5416,16 +5899,11 @@ } } }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true }, "get-stream": { "version": "5.2.0", @@ -5450,9 +5928,9 @@ "dev": true }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -5473,9 +5951,9 @@ } }, "globals": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -5490,9 +5968,9 @@ } }, "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -5582,18 +6060,41 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "html-to-text": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-8.0.0.tgz", + "integrity": "sha512-fEtul1OerF2aMEV+Wpy+Ue20tug134jOY1GIudtdqZi7D0uTudB2tVJBKfVhTL03dtqeJoF8gk8EPX9SyMEvLg==", + "requires": { + "@selderee/plugin-htmlparser2": "^0.6.0", + "deepmerge": "^4.2.2", + "he": "^1.2.0", + "htmlparser2": "^6.1.0", + "minimist": "^1.2.5", + "selderee": "^0.6.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, "http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", @@ -5618,6 +6119,16 @@ "resolve-alpn": "^1.0.0" } }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -5678,28 +6189,19 @@ "dev": true }, "into-stream": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-5.1.1.tgz", - "integrity": "sha512-krrAJ7McQxGGmvaYbB7Q1mcA+cRwg9Ij2RfWIeVesNBgVDZmzY/Fa4IpZUT3bmdRzMzdf/mzltCG2Dq99IZGBA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", "dev": true, "requires": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" } }, - "is-boolean-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", - "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", - "dev": true, - "requires": { - "call-bind": "^1.0.0" - } - }, "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", "dev": true, "requires": { "has": "^1.0.3" @@ -5731,18 +6233,6 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true - }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -5864,10 +6354,10 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "lodash.truncate": { @@ -5906,16 +6396,16 @@ } }, "mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" }, "mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", "requires": { - "mime-db": "1.47.0" + "mime-db": "1.48.0" } }, "mimic-response": { @@ -5951,6 +6441,11 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, + "moo": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", + "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5967,13 +6462,26 @@ } }, "multistream": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/multistream/-/multistream-2.1.1.tgz", - "integrity": "sha512-xasv76hl6nr1dEy3lPvy7Ej7K/Lx3O/FCvwge8PeVJpciPPoNCbaANcNiBug3IpdvTveZUcAV0DJzdnUDMesNQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.1.0.tgz", + "integrity": "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==", "dev": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.5" + "once": "^1.4.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "napi-build-utils": { @@ -5988,10 +6496,21 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "requires": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + } + }, "node-abi": { - "version": "2.21.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.21.0.tgz", - "integrity": "sha512-smhrivuPqEM3H5LmnY3KU6HfYv0u4QklgAxfFyRNujKUzbUcYZ+Jc2EhukB9SRcD2VpqhxM7n/MIcp1Ua1/JMg==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.0.tgz", + "integrity": "sha512-g6bZh3YCKQRdwuO/tSZZYJAw622SjsRfJ2X0Iy4sSOHZ34/sPPdVBn8fev2tj7njzLwuqPw9uMtGsGkO5kIQvg==", "dev": true, "requires": { "semver": "^5.4.1" @@ -6005,6 +6524,12 @@ } } }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + }, "noop-logger": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", @@ -6012,9 +6537,9 @@ "dev": true }, "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.0.1.tgz", + "integrity": "sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ==" }, "npmlog": { "version": "4.1.2", @@ -6051,9 +6576,9 @@ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" }, "obop": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/obop/-/obop-0.2.0.tgz", - "integrity": "sha512-EpOqW06yOhfHJQrHioPW3nVOcGg2QaPbsxlP9QhxQnd9vpMpqXd1JZevF/8jPYmXhDklNxD49hQoyUANCZpuzA==" + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/obop/-/obop-0.2.1.tgz", + "integrity": "sha512-cw8GJUfhWqFK/NOEZfBUhn8mLrnyRfbWyK9MGl/hFaLQDh1QlaOapvnTsB8X+xWMFEi4pr5BCQXaiuDD2iFJww==" }, "once": { "version": "1.4.0", @@ -6077,12 +6602,6 @@ "word-wrap": "^1.2.3" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "p-any": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-any/-/p-any-3.0.0.tgz", @@ -6093,9 +6612,9 @@ } }, "p-cancelable": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", - "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" }, "p-is-promise": { "version": "3.0.0", @@ -6121,6 +6640,15 @@ "callsites": "^3.0.0" } }, + "parseley": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.7.0.tgz", + "integrity": "sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw==", + "requires": { + "moo": "^0.5.1", + "nearley": "^2.20.1" + } + }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", @@ -6150,9 +6678,9 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-type": { @@ -6172,78 +6700,105 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, "pkg": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/pkg/-/pkg-4.5.1.tgz", - "integrity": "sha512-UXKL88jGQ+FD4//PyrFeRcqurVQ3BVIfUNaEU9cXY24EJz08JyBj85qrGh0CFGvyzNb1jpwHOnns5Sw0M5H92Q==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.2.1.tgz", + "integrity": "sha512-kQ5Fla+76rdmFJNrEOgoklxAURl3uvhvX+m4LCQCRrI+q2lOgsx9pS02NhNuDVnyXsthluAuXCh5SKcMyw+cWw==", "dev": true, "requires": { - "@babel/parser": "7.13.12", - "@babel/runtime": "7.13.10", - "chalk": "^3.0.0", - "escodegen": "^1.14.1", - "fs-extra": "^8.1.0", - "globby": "^11.0.0", - "into-stream": "^5.1.1", + "@babel/parser": "7.13.13", + "@babel/types": "7.13.12", + "chalk": "^4.1.0", + "escodegen": "^2.0.0", + "fs-extra": "^9.1.0", + "globby": "^11.0.3", + "into-stream": "^6.0.0", "minimist": "^1.2.5", - "multistream": "^2.1.1", - "pkg-fetch": "2.6.9", + "multistream": "^4.1.0", + "pkg-fetch": "3.1.1", "prebuild-install": "6.0.1", "progress": "^2.0.3", - "resolve": "^1.15.1", - "stream-meter": "^1.0.4" + "resolve": "^1.20.0", + "stream-meter": "^1.0.4", + "tslib": "2.1.0" }, "dependencies": { - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true } } }, "pkg-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-2.6.9.tgz", - "integrity": "sha512-EnVR8LRILXBvaNP+wJOSY02c3+qDDfyEyR+aqAHLhcc9PBnbxFT9UZ1+If49goPQzQPn26TzF//fc6KXZ0aXEg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.1.1.tgz", + "integrity": "sha512-3GfpNwbwoTxge2TrVp6Oyz/FZJOoxF1r0+1YikOhnBXa2Di/VOJKtUObFHap76BFnyFo1fwh5vARWFR9TzLKUg==", "dev": true, "requires": { - "@babel/runtime": "^7.9.2", - "byline": "^5.0.0", - "chalk": "^3.0.0", - "expand-template": "^2.0.3", - "fs-extra": "^8.1.0", - "minimist": "^1.2.5", + "chalk": "^4.1.0", + "fs-extra": "^9.1.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", "progress": "^2.0.3", - "request": "^2.88.0", - "request-progress": "^3.0.0", - "semver": "^6.3.0", - "unique-temp-dir": "^1.0.0" + "semver": "^7.3.5", + "yargs": "^16.2.0" }, "dependencies": { - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true } } @@ -6277,6 +6832,22 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", + "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "dev": true, + "peer": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -6340,6 +6911,20 @@ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=" + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -6374,16 +6959,10 @@ "util-deprecate": "~1.0.1" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "request": { @@ -6424,14 +7003,11 @@ } } }, - "request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "dev": true, - "requires": { - "throttleit": "^1.0.0" - } + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true }, "require-from-string": { "version": "2.0.2", @@ -6468,6 +7044,11 @@ "lowercase-keys": "^2.0.0" } }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -6520,6 +7101,14 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "selderee": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.6.0.tgz", + "integrity": "sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg==", + "requires": { + "parseley": "^0.7.0" + } + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -6756,26 +7345,23 @@ } }, "table": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.1.0.tgz", - "integrity": "sha512-T4G5KMmqIk6X87gLKWyU5exPpTjLjY5KyrFWaIjv3SvgaIUGXV7UEzGEnZJdTA38/yUS6f9PlKezQ0bYXG3iIQ==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", "dev": true, "requires": { "ajv": "^8.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", "lodash.clonedeep": "^4.5.0", - "lodash.flatten": "^4.4.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" }, "dependencies": { "ajv": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.1.0.tgz", - "integrity": "sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -6836,17 +7422,17 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, "to-array": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6883,9 +7469,9 @@ } }, "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", "dev": true }, "tsutils": { @@ -6895,6 +7481,14 @@ "dev": true, "requires": { "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } } }, "tunnel-agent": { @@ -6930,15 +7524,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", - "dev": true - }, - "uid2": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", - "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz", + "integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==", "dev": true }, "ultron": { @@ -6946,17 +7534,6 @@ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" }, - "unique-temp-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz", - "integrity": "sha1-bc6VsmgcoAPuv7MEpBX5y6vMU4U=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1", - "os-tmpdir": "^1.0.1", - "uid2": "0.0.3" - } - }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -7064,6 +7641,17 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -7098,11 +7686,38 @@ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/package.json b/package.json index 2b7c59c..9e37894 100644 --- a/package.json +++ b/package.json @@ -1,45 +1,52 @@ { - "name": "floatplane-plex-downloader", - "version": "5.0.0", - "private": true, - "scripts": { - "prep": "npm install && npm run build", - "start": "tsc && node ./dist/float.js", - "test-docker": "tsc && node ./dist/float.js --docker", - "build": "tsc && pkg ./dist/float.js --out-path=./build -t latest-linux,latest-mac,latest-win" - }, - "dependencies": { - "@ctrl/plex": "^1.5.3", - "@inrixia/db": "^1.7.0", - "@inrixia/helpers": "^1.15.1", - "ffbinaries": "^1.1.4", - "floatplane": "^3.0.5", - "got": "^11.8.2", - "multi-progress-bars": "^3.2.3", - "process.argv": "^0.6.0", - "prompts": "^2.4.0", - "sanitize-filename": "^1.6.3", - "semver": "^7.3.5", - "tough-cookie": "^4.0.0", - "tough-cookie-file-store": "^2.0.2" - }, - "pkg": { - "scripts": "./float.js", - "targets": [ - "node10-linux-x64", - "node10-macos-x64", - "node10-win-x64" - ] - }, - "devDependencies": { - "@types/multi-progress": "^2.0.3", - "@types/prompts": "^2.0.9", - "@types/semver": "^7.3.4", - "@types/tough-cookie-file-store": "^2.0.1", - "@typescript-eslint/eslint-plugin": "^4.18.0", - "@typescript-eslint/parser": "^4.18.0", - "eslint": "^7.22.0", - "pkg": "^4.4.9", - "typescript": "^4.2.3" - } + "name": "floatplane-plex-downloader", + "version": "5.1.0", + "private": true, + "scripts": { + "prep": "npm install && npm run build", + "prestart": "tsc", + "start": "node ./dist/float.js", + "startHeadless": "tsc && node ./dist/float.js --headless", + "build": "tsc && pkg ./dist/float.js --out-path=./build -t latest-linux,latest-mac,latest-win", + "buildLinux": "tsc && pkg ./dist/float.js --out-path=./build -t latest-linux", + "buildMac": "tsc && pkg ./dist/float.js --out-path=./build -t latest-mac", + "buildWin": "tsc && pkg ./dist/float.js --out-path=./build -t latest-win" + }, + "dependencies": { + "@ctrl/plex": "^1.5.3", + "@inrixia/db": "^1.7.3", + "@inrixia/helpers": "^1.20.2", + "ffbinaries": "^1.1.4", + "floatplane": "^3.1.8", + "got": "^11.8.2", + "html-to-text": "^8.0.0", + "multi-progress-bars": "^3.2.4", + "process.argv": "^0.6.0", + "prompts": "^2.4.1", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.5", + "tough-cookie": "^4.0.0", + "tough-cookie-file-store": "^2.0.2" + }, + "pkg": { + "scripts": "./float.js", + "targets": [ + "node10-linux-x64", + "node10-macos-x64", + "node10-win-x64" + ] + }, + "devDependencies": { + "@types/html-to-text": "^8.0.0", + "@types/multi-progress": "^2.0.3", + "@types/prompts": "^2.0.13", + "@types/semver": "^7.3.6", + "@types/tough-cookie-file-store": "^2.0.1", + "@typescript-eslint/eslint-plugin": "^4.27.0", + "@typescript-eslint/parser": "^4.27.0", + "eslint": "^7.29.0", + "eslint-plugin-prettier": "^3.4.0", + "pkg": "^5.2.1", + "typescript": "^4.3.4" + } } diff --git a/src/downloader.ts b/src/downloader.ts index 9b86680..7e8279d 100644 --- a/src/downloader.ts +++ b/src/downloader.ts @@ -1,31 +1,26 @@ -import { MultiProgressBars } from "multi-progress-bars"; -import Video from "./lib/Video"; +import { MultiProgressBars, UpdateOptions } from 'multi-progress-bars'; +import Video from './lib/Video'; -import { settings } from "./lib/helpers"; - -import { Resolution } from "./lib/types"; - -import { promisify } from "util"; -const sleep = promisify(setTimeout); +import { settings, args } from './lib/helpers'; type promiseFunction = (f: Promise) => void; -const reset = "\u001b[0m"; -const cy = (str: string|number) => `\u001b[36;1m${str}\u001b[0m`; -const gr = (str: string|number) => `\u001b[32;1m${str}\u001b[0m`; -const ye = (str: string|number) => `\u001b[33;1m${str}\u001b[0m`; -const bl = (str: string|number) => `\u001b[34;1m${str}\u001b[0m`; +const reset = '\u001b[0m'; +const cy = (str: string | number) => `\u001b[36;1m${str}\u001b[0m`; +const gr = (str: string | number) => `\u001b[32;1m${str}\u001b[0m`; +const ye = (str: string | number) => `\u001b[33;1m${str}\u001b[0m`; +const bl = (str: string | number) => `\u001b[34;1m${str}\u001b[0m`; -export default class VideoProcessor { +export default class Downloader { private mpb?: MultiProgressBars; - private videoQueue: Array<{ video: Video, res: promiseFunction }>; + private videoQueue: Array<{ video: Video; res: promiseFunction }>; private videosProcessing: number; private videosProcessed: number; - private downloadStats: { [key: string]: { totalMB: number, downloadedMB: number, downloadSpeed: number, timeElapsed: number } }; + private downloadStats: { [key: string]: { totalMB: number; downloadedMB: number; downloadSpeed: number } }; private runQueue: boolean; - constructor () { + constructor() { this.videoQueue = []; this.videosProcessing = 0; this.videosProcessed = 0; @@ -43,7 +38,7 @@ export default class VideoProcessor { private tickQueue(): void { if (this.runQueue === false) return; - while(this.videoQueue.length > 0 && (settings.downloadThreads === -1 || this.videosProcessing < settings.downloadThreads)) { + while (this.videoQueue.length > 0 && (settings.downloadThreads === -1 || this.videosProcessing < settings.downloadThreads)) { this.videosProcessing++; const task = this.videoQueue.pop(); if (task !== undefined) { @@ -62,11 +57,11 @@ export default class VideoProcessor { processVideos(videos: Video[]): Array> { if (videos.length !== 0) { console.log(`> Processing ${videos.length} videos...`); - this.mpb = new MultiProgressBars({ initMessage: "", anchor: "top" }); + if (args.headless !== true) this.mpb = new MultiProgressBars({ initMessage: '', anchor: 'top' }); this.downloadStats = {}; this.videosProcessed = 0; } - const processingPromises = videos.map(video => new Promise(res => this.videoQueue.push({video, res}))); + const processingPromises = videos.reverse().map((video) => new Promise((res) => this.videoQueue.push({ video, res }))); // Handler for when all downloads are done. if (videos.length !== 0) Promise.all(processingPromises).then(() => this.updateSummaryBar()); return processingPromises; @@ -74,101 +69,104 @@ export default class VideoProcessor { private updateSummaryBar(): void { if (this.downloadStats === undefined) return; - const { totalMB, downloadedMB, downloadSpeed, timeElapsed } = Object.values(this.downloadStats).reduce((summary, stats) => { - for (const key in stats) { summary[key as keyof typeof stats] += stats[key as keyof typeof stats]; } - return summary; - }, { totalMB: 0, downloadedMB: 0, downloadSpeed: 0, timeElapsed: 0}); + const { totalMB, downloadedMB, downloadSpeed } = Object.values(this.downloadStats).reduce( + (summary, stats) => { + for (const key in stats) { + summary[key as keyof typeof stats] += stats[key as keyof typeof stats]; + } + return summary; + }, + { totalMB: 0, downloadedMB: 0, downloadSpeed: 0 } + ); // (videos remaining * avg time to download a video) - const totalVideos = this.videoQueue.length+this.videosProcessed+this.videosProcessing; - const summaryDownloadETA = (this.videoQueue.length+this.videosProcessing) * (timeElapsed/this.videosProcessed); - const whitespace = " "; - const processed = `Processed: ${ye(this.videosProcessed)}/${ye(totalVideos)}${whitespace}`; - const downloaded = `Total Downloaded: ${cy(downloadedMB.toFixed(2))}/${cy(totalMB.toFixed(2)+"MB")}${whitespace}`; - const speed = `Download Speed: ${gr((downloadSpeed/1024000).toFixed(2)+"Mb/s")}${whitespace}`; - const eta = `Rough ETA: ${bl(Math.floor(summaryDownloadETA / 60))} minutes${whitespace}`; - process.stdout.write(" "); - process.stdout.write(`\n${processed}\n${downloaded}\n${speed}\n${isNaN(summaryDownloadETA)?"":eta}\n\n\n`); + const totalVideos = this.videoQueue.length + this.videosProcessed + this.videosProcessing; + const whitespace = ' '; + const processed = `Processed: ${ye(this.videosProcessed)}/${ye(totalVideos)}${whitespace}`; + const downloaded = `Total Downloaded: ${cy(downloadedMB.toFixed(2))}/${cy(totalMB.toFixed(2) + 'MB')}${whitespace}`; + const speed = `Download Speed: ${gr(((downloadSpeed / 1024000) * 8).toFixed(2) + 'Mb/s')}${whitespace}`; + process.stdout.write(' '); + process.stdout.write(`\n${processed}\n${downloaded}\n${speed}\n\n\n`); } - private async processVideo(video: Video, retries = 0, quality: Resolution = settings.floatplane.videoResolution): Promise { - let formattedTitle: string; - if (video.channel.consoleColor !== undefined) formattedTitle = `${video.channel.consoleColor}${video.channel.title}${reset} - ${video.title}`.slice(0, 32+video.channel.consoleColor.length+reset.length); - else formattedTitle = `${video.channel.title} - ${video.title}`.slice(0, 32); - - if (this.downloadStats !== undefined) while (formattedTitle in this.downloadStats) formattedTitle = ` ${formattedTitle}`.slice(0, 32); - - if (this.mpb === undefined) throw new Error("Progressbar failed to initialize! Cannot continue download"); + /** + * Updsate the progress bar for a specific video. + * @param formattedTitle Title of bar to update. + * @param barUpdate Update object to update the bar with. + * @param displayNow If the update should be immediately sent to console (Only applied if running in headless mode) + */ + private updateBar(formattedTitle: string, barUpdate: UpdateOptions, displayNow = false): void { + if (args.headless === true) { + if (displayNow === true && barUpdate.message !== undefined) console.log(`${formattedTitle} - ${barUpdate.message}`); + } else if (this.mpb !== undefined) this.mpb.updateTask(formattedTitle, barUpdate); + } - this.mpb.addTask(formattedTitle, { - type: "percentage", - barColorFn: str => `${video.channel.consoleColor||""}${str}` - }); + private async processVideo(video: Video, retries = 0, allowRangeQuery = true): Promise { + let formattedTitle: string; + if (args.headless === true) formattedTitle = `${video.channel.title} - ${video.title}`; + else if (video.channel.consoleColor !== undefined) { + formattedTitle = `${video.channel.consoleColor}${video.channel.title}${reset} - ${video.title}`.slice( + 0, + 32 + video.channel.consoleColor.length + reset.length + ); + } else formattedTitle = `${video.channel.title} - ${video.title}`.slice(0, 32); + + if (this.downloadStats !== undefined) while (formattedTitle in this.downloadStats) formattedTitle = `.${formattedTitle}`.slice(0, 32); + + if (args.headless === true) console.log(`${formattedTitle} - Downloading...`); + else { + if (this.mpb === undefined) throw new Error('Progressbar failed to initialize! Cannot continue download'); + this.mpb.addTask(formattedTitle, { + type: 'percentage', + barColorFn: (str) => `${video.channel.consoleColor || ''}${str}`, + }); + } try { // If the video is already downloaded then just mux its metadata - if (!await video.isMuxed() && !await video.isDownloaded()) { + if (!(await video.isMuxed()) && !(await video.isDownloaded())) { const startTime = Date.now(); - const downloadRequest = await video.download(quality); - downloadRequest.on("downloadProgress", downloadProgress => { - const totalMB = downloadProgress.total/1024000; - const downloadedMB = (downloadProgress.transferred/1024000); + const downloadRequest = await video.download(settings.floatplane.videoResolution as string, allowRangeQuery); + downloadRequest.on('downloadProgress', (downloadProgress) => { + const totalMB = downloadProgress.total / 1024000; + const downloadedMB = downloadProgress.transferred / 1024000; const timeElapsed = (Date.now() - startTime) / 1000; - const downloadSpeed = downloadProgress.transferred/timeElapsed; - const downloadETA = (downloadProgress.total / downloadSpeed) - timeElapsed; // Round to 4 decimals - if (this.mpb !== undefined) this.mpb.updateTask(formattedTitle, { - percentage: downloadProgress.percent, - message: `${reset}${cy(downloadedMB.toFixed(2))}/${cy(totalMB.toFixed(2)+"MB")} ${gr((downloadSpeed/1024000).toFixed(2)+"Mb/s")} ETA: ${bl(Math.floor(downloadETA / 60)+"m "+(Math.floor(downloadETA) % 60)+"s")}` + const downloadSpeed = downloadProgress.transferred / timeElapsed; + const downloadETA = downloadProgress.total / downloadSpeed - timeElapsed; // Round to 4 decimals + this.updateBar(formattedTitle, { + percentage: downloadProgress.percent, + message: `${reset}${cy(downloadedMB.toFixed(2))}/${cy(totalMB.toFixed(2) + 'MB')} ${gr(((downloadSpeed / 1024000) * 8).toFixed(2) + 'Mb/s')} ETA: ${bl( + Math.floor(downloadETA / 60) + 'm ' + (Math.floor(downloadETA) % 60) + 's' + )}`, }); - this.downloadStats[formattedTitle] = { totalMB, downloadedMB, downloadSpeed, timeElapsed: 0 }; - this.updateSummaryBar(); + this.downloadStats[formattedTitle] = { totalMB, downloadedMB, downloadSpeed }; + if (args.headless !== true) this.updateSummaryBar(); }); await new Promise((res, rej) => { - downloadRequest.on("end", res); - downloadRequest.on("error", rej); + downloadRequest.on('end', res); + downloadRequest.on('error', rej); }); - this.downloadStats[formattedTitle].timeElapsed = (Date.now() - startTime) / 1000; this.downloadStats[formattedTitle].downloadSpeed = 0; } - if (!await video.isMuxed()) { - this.mpb.updateTask(formattedTitle, { - percentage: 0.99, - message: "Muxing ffmpeg metadata..." + if (!(await video.isMuxed())) { + this.updateBar(formattedTitle, { + percentage: 0.99, + message: 'Muxing ffmpeg metadata...', }); await video.muxffmpegMetadata(); } - this.mpb.done(formattedTitle); + if (args.headless === true) { + console.log(`${formattedTitle} - Downloaded!`); + this.updateSummaryBar(); + } else if (this.mpb !== undefined) this.mpb.done(formattedTitle); + if (settings.postProcessingCommand !== '') + await video.postProcessingCommand().catch((err) => console.log(`An error occurred while executing the postProcessingCommand!\n${err.message}\n`)); } catch (error) { // Handle errors when downloading nicely - this.mpb.updateTask(formattedTitle, { message: `\u001b[31m\u001b[1mERR\u001b[0m: ${error.message}` }); - - // Retry downloading the video if this is the first failure. - if (retries === 0) { - this.mpb.updateTask(formattedTitle, { message: `\u001b[31m\u001b[1mERR\u001b[0m: ${error.message} - Retrying...` }); - await sleep(1000); - await this.processVideo(video, ++retries); - return; - } - - // If the server aborted the request retry up to 3 times. - if (error.message.includes("The server aborted pending request") && retries < 3) { - this.mpb.updateTask(formattedTitle, { message: `\u001b[31m\u001b[1mERR\u001b[0m: ${error.message} - Retrying ${retries}/3` }); - await sleep(1000); - await this.processVideo(video, ++retries); - return; - } - - if (error.message.includes("Response code 400") || error.message.includes("Response code 404")) { - // Drop down the qualities until one works or give up - const currentResIndex = settings.floatplane._avalibleResolutions.indexOf(quality); - if (currentResIndex !== 0) { - const newRes = settings.floatplane._avalibleResolutions[currentResIndex-1]; - this.mpb.updateTask(formattedTitle, { message: `\u001b[31m\u001b[1mERR\u001b[0m: ${error.message} - Retrying at ${newRes}p` }); - await sleep(1000); - await this.processVideo(video, retries, newRes); - return; - } - } - return; + if (retries < 3) { + this.updateBar(formattedTitle, { message: `\u001b[31m\u001b[1mERR\u001b[0m: ${error.message} - Retrying ${retries}/3` }, true); + if (error.message.indexOf('Range Not Satisfiable')) await this.processVideo(video, ++retries, false); + else await this.processVideo(video, ++retries); + } else this.updateBar(formattedTitle, { message: `\u001b[31m\u001b[1mERR\u001b[0m: ${error.message} Max Retries! ${retries}/3` }, true); } } -} \ No newline at end of file +} diff --git a/src/float.ts b/src/float.ts index f42e0e3..4dc1a26 100644 --- a/src/float.ts +++ b/src/float.ts @@ -1,46 +1,48 @@ -import { settings, fetchFFMPEG } from "./lib/helpers"; +import { quickStart, validatePlexSettings } from './quickStart'; +import { fetchSubscriptions } from './subscriptionFetching'; +import { settings, fetchFFMPEG } from './lib/helpers'; +import { MyPlexAccount } from '@ctrl/plex'; +import { fApi } from './lib/FloatplaneAPI'; +import { loginFloatplane } from './logins'; +import Downloader from './downloader'; +import { gt, diff } from 'semver'; -import { fApi } from "./lib/FloatplaneAPI"; - -import { quickStart, validatePlexSettings } from "./quickStart"; - -import { loginFloatplane } from "./logins"; - -import { fetchSubscriptions } from "./subscriptionFetching"; -import VideoProcessor from "./downloader"; - -import { MyPlexAccount } from "@ctrl/plex"; - -import type Subscription from "./lib/Subscription"; - -import { gt, diff } from "semver"; +import type Subscription from './lib/Subscription'; /** * Main function that triggeres everything else in the script */ -const fetchNewVideos = async (subscriptions: Array, videoProcessor: VideoProcessor) => { +const fetchNewVideos = async (subscriptions: Array, videoProcessor: Downloader) => { for (const subscription of subscriptions) { - await Promise.all(videoProcessor.processVideos(await subscription.fetchNewVideos(true, settings.floatplane.videosToSearch))); + await Promise.all(videoProcessor.processVideos(await subscription.fetchNewVideos(settings.floatplane.videosToSearch, settings.extras.stripSubchannelPrefix))); } if (settings.plex.enabled) { - process.stdout.write("> Refreshing plex sections... "); - const plexApi = await (new MyPlexAccount(undefined, undefined, undefined, settings.plex.token).connect()); + process.stdout.write('> Refreshing plex sections... '); + const plexApi = await new MyPlexAccount(undefined, undefined, undefined, settings.plex.token).connect(); for (const sectionToUpdate of settings.plex.sectionsToUpdate) { await (await (await (await (await plexApi.resource(sectionToUpdate.server)).connect()).library()).section(sectionToUpdate.section)).refresh(); } - process.stdout.write("\u001b[36mDone!\u001b[0m\n\n"); + process.stdout.write('\u001b[36mDone!\u001b[0m\n\n'); } }; // Async start (async () => { // eslint-disable-next-line @typescript-eslint/no-var-requires - const version: string = require("../package.json").version; - const latest = await fApi.got("https://raw.githubusercontent.com/Inrixia/Floatplane-Downloader/master/latest.json", { resolveBodyOnly: true }).then(JSON.parse).catch(() => ({ version })); + const version: string = require('../package.json').version; + const latest = await fApi + .got('https://raw.githubusercontent.com/Inrixia/Floatplane-Downloader/master/package.json', { resolveBodyOnly: true }) + .then(JSON.parse) + .catch(() => ({ version })); + + if (gt(latest.version, version)) + console.log( + `There is a ${diff(latest.version, version)} update avalible! ${version} > ${ + latest.version + }.\nHead to \u001b[36mhttps://github.com/Inrixia/Floatplane-Downloader/releases\u001b[0m to update!\n` + ); - if (gt(latest.version, version)) console.log(`There is a ${diff(latest.version, version)} update avalible! ${version} > ${latest.version}.\nHead to \u001b[36mhttps://github.com/Inrixia/Floatplane-Downloader/releases\u001b[0m to update!\n`); - await fetchFFMPEG(); // Earlybird functions, these are run before script start and not run again if script repeating is enabled. if (settings.runQuickstartPrompts) await quickStart(); @@ -56,26 +58,25 @@ const fetchNewVideos = async (subscriptions: Array, videoProcessor await loginFloatplane(); } - process.stdout.write("> Fetching user subscriptions... "); + process.stdout.write('> Fetching user subscriptions... '); const subscriptions = await fetchSubscriptions(); - process.stdout.write("\u001b[36mDone!\u001b[0m\n\n"); + process.stdout.write('\u001b[36mDone!\u001b[0m\n\n'); - const videoProcessor = new VideoProcessor(); - videoProcessor.start(); + const downloader = new Downloader(); + downloader.start(); - await fetchNewVideos(subscriptions, videoProcessor); - + await fetchNewVideos(subscriptions, downloader); if (settings.floatplane.waitForNewVideos === true) { - fApi.sails.on("syncEvent", syncEvent => { - if (syncEvent.event === "postRelease") fetchNewVideos(subscriptions, videoProcessor); + fApi.sails.on('syncEvent', (syncEvent) => { + if (syncEvent.event === 'postRelease') fetchNewVideos(subscriptions, downloader); }); - process.stdout.write("Connecting to floatplane notifications for new videos... "); + process.stdout.write('Connecting to floatplane notifications for new videos... '); process.stdout.write(`${(await fApi.sails.connect()).message}\n`); - } else videoProcessor.stop(); -})().catch(err => { - console.error("An error occurred!"); + } else downloader.stop(); +})().catch((err) => { + console.error('An error occurred!'); console.error(err); process.exit(1); -}); \ No newline at end of file +}); diff --git a/src/lib/Channel.ts b/src/lib/Channel.ts index 01c5ea2..582e169 100644 --- a/src/lib/Channel.ts +++ b/src/lib/Channel.ts @@ -1,23 +1,23 @@ -import db from "@inrixia/db"; -import Video from "./Video"; +import db from '@inrixia/db'; +import Video from './Video'; -import type { BlogPost } from "floatplane/creator"; -import type { ChannelOptions } from "./types"; -import type Subscription from "./Subscription"; +import type { BlogPost } from 'floatplane/creator'; +import type { ChannelOptions } from './types'; +import type Subscription from './Subscription'; // e = episodeNo, d = downloaded, s = filesize in bytes, f = file -export type VideoDBEntry = { episodeNo: number, expectedSize?: number } +export type VideoDBEntry = { episodeNo: number; expectedSize?: number }; export type ChannelDB = { - videos: { [key: string]: VideoDBEntry }, - nextEpisodeNo: number -} + videos: { [key: string]: VideoDBEntry }; + nextEpisodeNo: number; +}; export default class Channel { - public title: ChannelOptions["title"]; - public identifiers: ChannelOptions["identifiers"]; - public skip: ChannelOptions["skip"]; + public title: ChannelOptions['title']; + public identifiers: ChannelOptions['identifiers']; + public skip: ChannelOptions['skip']; - public consoleColor: ChannelOptions["consoleColor"] + public consoleColor: ChannelOptions['consoleColor']; public subscription: Subscription; @@ -34,10 +34,9 @@ export default class Channel { this.skip = channel.skip; this.consoleColor = channel.consoleColor; - const databaseFilePath = `./db/channels/${subscription.creatorId}/${channel.title}.json`; try { - this._db = db(databaseFilePath, { videos: {}, nextEpisodeNo: 1 }); + this._db = db(databaseFilePath, { template: { videos: {}, nextEpisodeNo: 1 } }); } catch { throw new Error(`Cannot load Channel database file ${databaseFilePath}! Please delete the file or fix it!`); } @@ -51,9 +50,9 @@ export default class Channel { this.subscription.updateLastSeenVideo({ guid, releaseDate }); } - public addVideo (video: BlogPost): Video { + public addVideo(video: BlogPost): Video { // Set the episode number this._db.videos[video.guid] ??= { episodeNo: this._db.nextEpisodeNo++ }; return new Video(video, this); } -} \ No newline at end of file +} diff --git a/src/lib/FloatplaneAPI.ts b/src/lib/FloatplaneAPI.ts index 0ac8d75..91da351 100644 --- a/src/lib/FloatplaneAPI.ts +++ b/src/lib/FloatplaneAPI.ts @@ -1,6 +1,6 @@ -import { FileCookieStore } from "tough-cookie-file-store"; -import { CookieJar } from "tough-cookie"; -export const cookieJar = new CookieJar(new FileCookieStore("./db/cookies.json")); +import { FileCookieStore } from 'tough-cookie-file-store'; +import { CookieJar } from 'tough-cookie'; +export const cookieJar = new CookieJar(new FileCookieStore('./db/cookies.json')); -import FloatplaneApi from "floatplane"; -export const fApi = new FloatplaneApi(cookieJar); \ No newline at end of file +import { Floatplane } from 'floatplane'; +export const fApi = new Floatplane(cookieJar); diff --git a/src/lib/Subscription.ts b/src/lib/Subscription.ts index 8754752..2a794f1 100644 --- a/src/lib/Subscription.ts +++ b/src/lib/Subscription.ts @@ -1,21 +1,19 @@ -import db from "@inrixia/db"; -import Channel from "./Channel"; +import { BlogPost } from 'floatplane/creator'; +import { fApi } from './FloatplaneAPI'; +import Channel from './Channel'; +import db from '@inrixia/db'; -import type { SubscriptionSettings } from "./types"; - -import { BlogPost } from "floatplane/creator"; -import { fApi } from "./FloatplaneAPI"; -import type Video from "./Video"; +import type { SubscriptionSettings } from './types'; +import type Video from './Video'; type LastSeenVideo = { - guid: BlogPost["guid"]; - releaseDate: BlogPost["releaseDate"]; -} + guid: BlogPost['guid']; + releaseDate: BlogPost['releaseDate']; +}; type SubscriptionDB = { lastSeenVideo: LastSeenVideo; videos: BlogPost[]; -} - +}; export default class Subscription { public channels: Channel[]; @@ -26,47 +24,50 @@ export default class Subscription { private _db: SubscriptionDB; constructor(subscription: SubscriptionSettings) { this.creatorId = subscription.creatorId; - - this.channels = Object.values(subscription.channels).map(channel => new Channel(channel, this)); + + this.channels = Object.values(subscription.channels).map((channel) => new Channel(channel, this)); this.defaultChannel = new Channel(subscription.channels._default, this); // Load/Create database const databaseFilePath = `./db/subscriptions/${subscription.creatorId}.json`; try { - this._db = db(databaseFilePath, { lastSeenVideo: { guid: "", releaseDate: "" }, videos: [] }); + this._db = db(databaseFilePath, { template: { lastSeenVideo: { guid: '', releaseDate: '' }, videos: [] } }); } catch { throw new Error(`Cannot load Subscription database file ${databaseFilePath}! Please delete the file or fix it!`); } } - get lastSeenVideo(): SubscriptionDB["lastSeenVideo"] { + get lastSeenVideo(): SubscriptionDB['lastSeenVideo'] { return this._db.lastSeenVideo; } public updateLastSeenVideo(videoSeen: LastSeenVideo): void { - if (this.lastSeenVideo.releaseDate === "" || new Date(videoSeen.releaseDate) > new Date(this.lastSeenVideo.releaseDate)) this._db.lastSeenVideo = videoSeen; + if (this.lastSeenVideo.releaseDate === '' || new Date(videoSeen.releaseDate) > new Date(this.lastSeenVideo.releaseDate)) this._db.lastSeenVideo = videoSeen; } /** * @param {fApiVideo} video */ - public addVideo(video: BlogPost, overrideSkip: true): ReturnType - public addVideo(video: BlogPost, overrideSkip?: false): ReturnType | null - - public addVideo(video: BlogPost, overrideSkip=false): ReturnType | null { + public addVideo(video: BlogPost, overrideSkip: true, stripSubchannelPrefix?: boolean): ReturnType; + public addVideo(video: BlogPost, overrideSkip?: false, stripSubchannelPrefix?: boolean): ReturnType | null; + + public addVideo(video: BlogPost, overrideSkip = false, stripSubchannelPrefix = true): ReturnType | null { for (const channel of this.channels) { // Check if the video belongs to this channel if (channel.identifiers === false) continue; for (const identifier of channel.identifiers) { - if (typeof identifier.type !== "string") throw new Error(`Video value for channel identifier type ${video[identifier.type]} on channel ${channel.title} is of type ${typeof video[identifier.type]} not string!`); + if (typeof identifier.type !== 'string') + throw new Error( + `Video value for channel identifier type ${video[identifier.type]} on channel ${channel.title} is of type ${typeof video[identifier.type]} not string!` + ); else { // Description is named text on videos, kept description for ease of use for users but have to change it here... - const identifierType = identifier.type === "description" ? "text" : identifier.type; + const identifierType = identifier.type === 'description' ? 'text' : identifier.type; if ((video[identifierType] as string).toLowerCase().indexOf(identifier.check.toLowerCase()) !== -1) { if (overrideSkip === false && channel.skip === true) return null; // Remove the identifier from the video title if to give a nicer title - if (identifierType === "title") video.title = video.title.replace(identifier.check, "").trim(); + if (identifierType === 'title' && stripSubchannelPrefix === true) video.title = video.title.replace(identifier.check, '').trim(); return channel.addVideo(video); } } @@ -76,29 +77,31 @@ export default class Subscription { return this.defaultChannel.addVideo(video); } - public async fetchNewVideos(logProgress=false, videosToSearch=20): Promise> { - const coloredTitle = `${this.defaultChannel.consoleColor||"\u001b[38;5;208m"}${this.defaultChannel.title}\u001b[0m`; + public async fetchNewVideos(videosToSearch = 20, stripSubchannelPrefix: boolean): Promise> { + const coloredTitle = `${this.defaultChannel.consoleColor || '\u001b[38;5;208m'}${this.defaultChannel.title}\u001b[0m`; const videos = []; - if (logProgress === true) process.stdout.write(`> Fetching latest videos from [${coloredTitle}]... Fetched ${videos.length} videos!`); + process.stdout.write(`> Fetching latest videos from [${coloredTitle}]... Fetched ${videos.length} videos!`); - for await (const video of fApi.creator.blogPostsIterable(this.creatorId, { type: "video" })) { + for await (const video of fApi.creator.blogPostsIterable(this.creatorId, { type: 'video' })) { if (video.guid === this.lastSeenVideo.guid) { - if (!await (this.addVideo(video, true)).isDownloaded()) this.lastSeenVideo.guid = ""; + if (!(await this.addVideo(video, true, stripSubchannelPrefix).isDownloaded())) this.lastSeenVideo.guid = ''; else break; } - if (this.lastSeenVideo.guid === "" && videos.length >= videosToSearch) break; + if (this.lastSeenVideo.guid === '' && videos.length >= videosToSearch) break; videos.push(video); - if (logProgress === true) process.stdout.write(`\r> Fetching latest videos from [${coloredTitle}]... Fetched ${videos.length} videos!`); + process.stdout.write(`\r> Fetching latest videos from [${coloredTitle}]... Fetched ${videos.length} videos!`); } // Make sure videos are in correct order for episode numbering, null episodes are part of a channel that is marked to be skipped const incompleteVideos: Video[] = []; - for (const video of videos.sort((a, b) => (+new Date(a.releaseDate)) - (+new Date(b.releaseDate))).map(video => this.addVideo(video))) { - if (video !== null && !await video.isMuxed()) incompleteVideos.push(video); + for (const video of videos + .sort((a, b) => +new Date(a.releaseDate) - +new Date(b.releaseDate)) + .map((video) => this.addVideo(video, false, stripSubchannelPrefix))) { + if (video !== null && !(await video.isMuxed())) incompleteVideos.push(video); } - process.stdout.write(` Skipped ${videos.length-incompleteVideos.length}.\n`); + process.stdout.write(` Skipped ${videos.length - incompleteVideos.length}.\n`); return incompleteVideos; } -} \ No newline at end of file +} diff --git a/src/lib/Video.ts b/src/lib/Video.ts index 51aed16..09bcd14 100644 --- a/src/lib/Video.ts +++ b/src/lib/Video.ts @@ -1,32 +1,35 @@ -import fs from "fs/promises"; -import { execFile } from "child_process"; -import { createWriteStream } from "fs"; +import { exec as execCallback, execFile } from 'child_process'; +import { createWriteStream } from 'fs'; +import { promisify } from 'util'; +import fs from 'fs/promises'; -import { settings } from "./helpers"; +const exec = promisify(execCallback); -import sanitize from "sanitize-filename"; -import builder from "xmlbuilder"; +import { settings } from './helpers'; -import type { BlogPost } from "floatplane/creator"; -import type Request from "got/dist/source/core"; +import { htmlToText } from 'html-to-text'; +import sanitize from 'sanitize-filename'; +import builder from 'xmlbuilder'; -import type Channel from "./Channel"; -import { fApi } from "./FloatplaneAPI"; +import { nPad } from '@inrixia/helpers/math'; +import { fApi } from './FloatplaneAPI'; + +import type { FilePathFormattingOptions } from './types'; +import type { BlogPost } from 'floatplane/creator'; +import type Request from 'got/dist/source/core'; +import type Channel from './Channel'; export default class Video { - public guid: BlogPost["guid"]; - public title: BlogPost["title"]; - public description: BlogPost["text"]; + public guid: BlogPost['guid']; + public title: BlogPost['title']; + public description: BlogPost['text']; public releaseDate: Date; - public thumbnail: BlogPost["thumbnail"]; + public thumbnail: BlogPost['thumbnail']; - public videoAttachments: BlogPost["videoAttachments"] + public videoAttachments: BlogPost['videoAttachments']; public channel: Channel; - public filePath: string; - private folderPath: string; - constructor(video: BlogPost, channel: Channel) { this.channel = channel; @@ -36,36 +39,56 @@ export default class Video { this.description = video.text; this.releaseDate = new Date(video.releaseDate); this.thumbnail = video.thumbnail; + } + + private formatString(string: string): string { + const formatLookup: FilePathFormattingOptions = { + '%channelTitle%': this.channel.title, + '%episodeNumber%': this.channel.lookupVideoDB(this.guid).episodeNo.toString(), + '%year%': this.releaseDate.getFullYear().toString(), + '%month%': nPad(this.releaseDate.getMonth() + 1), + '%day%': nPad(this.releaseDate.getDate()), + '%hour%': nPad(this.releaseDate.getHours()), + '%minute%': nPad(this.releaseDate.getMinutes()), + '%second%': nPad(this.releaseDate.getSeconds()), + '%videoTitle%': this.title.replace(/ - /g, ' ').replace(/\//g, ' ').replace(/\\/g, ' '), + }; + + for (const [match, value] of Object.entries(formatLookup)) { + string = string.replace(new RegExp(match, 'g'), value); + } + + return string; + } + + private get fullPath(): string { + return this.formatString(settings.filePathFormatting); + } + + private get folderPath(): string { + return this.fullPath.split('/').slice(0, -1).join('/'); + } - const YEAR = this.releaseDate.getFullYear(); - const MONTH = this.releaseDate.getMonth()>9?"0"+this.releaseDate.getMonth():this.releaseDate.getMonth(); // If the month is less than 10 pad it with a 0 - const fullPath = `${settings.filePathFormatting - .replace(/%channelTitle%/g, this.channel.title) - .replace(/%episodeNumber%/g, this.channel.lookupVideoDB(this.guid).episodeNo.toString()) - .replace(/%year%/g, YEAR.toString()) - .replace(/%month%/g, MONTH.toString()) - .replace(/%videoTitle%/g, this.title.replace(/ - /g, " ").replace(/\//g, " ").replace(/\\/g, " ")) - }`; - this.folderPath = fullPath.split("/").slice(0, -1).join("/"); - this.filePath = `${this.folderPath}/${sanitize(fullPath.split("/").slice(-1)[0])}`; + public get filePath(): string { + return `${this.folderPath}/${sanitize(this.fullPath.split('/').slice(-1)[0])}`; } - get expectedSize(): number|undefined { + get expectedSize(): number | undefined { return this.channel.lookupVideoDB(this.guid).expectedSize; } - set expectedSize(expectedSize: number|undefined) { + set expectedSize(expectedSize: number | undefined) { this.channel.lookupVideoDB(this.guid).expectedSize = expectedSize; } static getFileBytes = async (path: string): Promise => (await fs.stat(path).catch(() => ({ size: -1 }))).size; - public downloadedBytes = async (): Promise => Video.getFileBytes(this.filePath); - public isDownloaded = async (): Promise => await this.isMuxed() || await this.downloadedBytes() === this.expectedSize; + public downloadedBytes = async (): Promise => Video.getFileBytes(`${this.filePath}.partial`); + public isDownloaded = async (): Promise => (await this.isMuxed()) || (await this.downloadedBytes()) === this.expectedSize; public muxedBytes = async (): Promise => Video.getFileBytes(`${this.filePath}.mp4`); - public isMuxed = async (): Promise => await this.muxedBytes() === this.expectedSize; + public isMuxed = async (): Promise => (await this.muxedBytes()) === this.expectedSize; - public async download (quality: string): Promise { + public async download(quality: string, allowRangeQuery = true): Promise { if (await this.isDownloaded()) throw new Error(`Attempting to download "${this.title}" video already downloaded!`); // Make sure the folder for the video exists @@ -73,88 +96,116 @@ export default class Video { // If downloading artwork is enabled download it if (settings.extras.downloadArtwork && this.thumbnail !== undefined) { - fApi.got.stream(this.thumbnail.path).pipe(createWriteStream(`${this.filePath}.png`)); + fApi.got.stream(this.thumbnail.path).pipe(createWriteStream(`${this.filePath}${settings.artworkSuffix}.png`)); } // Save the thumbnail with the same name as the video so plex will use it if (settings.extras.saveNfo) { - const nfo = builder.create("episodedetails") - .ele("title").text(this.title).up() - .ele("showtitle").text(this.channel.title).up() - .ele("description").text(this.description).up() - .ele("aired").text(this.releaseDate.toString()).up() - .ele("season").text("1").up() - .ele("episode").text(this.channel.lookupVideoDB(this.guid).episodeNo.toString()).up() + const nfo = builder + .create('episodedetails') + .ele('title') + .text(this.title) + .up() + .ele('showtitle') + .text(this.channel.title) + .up() + .ele('description') + .text(htmlToText(this.description)) + .up() + .ele('aired') + .text(this.releaseDate.toString()) + .up() + .ele('season') + .text('1') + .up() + .ele('episode') + .text(this.channel.lookupVideoDB(this.guid).episodeNo.toString()) + .up() .end({ pretty: true }); - await fs.writeFile(`${this.filePath}.nfo`, nfo, "utf8"); + await fs.writeFile(`${this.filePath}.nfo`, nfo, 'utf8'); } - + // Handle download resumption if video was partially downloaded - const downloadedBytes = await this.downloadedBytes(); - const [writeStreamOptions, requestOptions] = downloadedBytes !== -1 ? [ - { start: downloadedBytes, flags: "r+" }, - { headers: { range: `bytes=${downloadedBytes}-${this.expectedSize}` } } - ] : [ - undefined, - undefined - ]; + let writeStreamOptions, requestOptions, downloadedBytes; + if (allowRangeQuery && this.expectedSize !== undefined && (downloadedBytes = await this.downloadedBytes()) !== -1) { + [writeStreamOptions, requestOptions] = [{ start: downloadedBytes, flags: 'r+' }, { headers: { range: `bytes=${downloadedBytes}-${this.expectedSize}` } }]; + } // Send download request video, assume the first video attached is the actual video as most will not have more than one video - const cdnInfo = await fApi.cdn.delivery("download", this.videoAttachments[0]); + const cdnInfo = await fApi.cdn.delivery('download', this.videoAttachments[0]); // Pick a random edge to download off, eventual even distribution const downloadEdge = cdnInfo.edges[Math.floor(Math.random() * cdnInfo.edges.length)]; // Convert the qualities into an array of resolutions - const avalibleQualities = cdnInfo.resource.data.qualityLevels.map(quality => quality.name); + const avalibleQualities = cdnInfo.resource.data.qualityLevels.map((quality) => quality.name); // Set the quality to use based on whats given in the settings.json or the highest avalible - const downloadQuality = avalibleQualities.includes(quality) ? quality : avalibleQualities[avalibleQualities.length-1]; + const downloadQuality = avalibleQualities.includes(quality) ? quality : avalibleQualities[avalibleQualities.length - 1]; - const downloadRequest = fApi.got.stream(`https://${downloadEdge.hostname}${cdnInfo.resource.uri.replace("{qualityLevels}", downloadQuality).replace("{token}", cdnInfo.resource.data.token)}`, requestOptions); + const downloadRequest = fApi.got.stream( + `https://${downloadEdge.hostname}${cdnInfo.resource.uri.replace('{qualityLevels}', downloadQuality).replace('{token}', cdnInfo.resource.data.token)}`, + requestOptions + ); // Pipe the download to the file once response starts - downloadRequest.pipe(createWriteStream(`${this.filePath}`, writeStreamOptions)); + downloadRequest.pipe(createWriteStream(`${this.filePath}.partial`, writeStreamOptions)); // Set the videos expectedSize once we know how big it should be for download validation. - if (this.expectedSize === undefined) downloadRequest.once("downloadProgress", progress => this.expectedSize = progress.total); - + if (this.expectedSize === undefined) downloadRequest.once('downloadProgress', (progress) => (this.expectedSize = progress.total)); + return downloadRequest; } public async markCompleted(): Promise { - if (!await this.isMuxed()) throw new Error(`Cannot mark ${this.title} as completed as video file size is not correct. Expected: ${this.expectedSize} bytes, Got: ${await this.muxedBytes()} bytes...`); + if (!(await this.isMuxed())) + throw new Error( + `Cannot mark ${this.title} as completed as video file size is not correct. Expected: ${this.expectedSize} bytes, Got: ${await this.muxedBytes()} bytes...` + ); return this.channel.markVideoCompleted(this.guid, this.releaseDate.toString()); } - public async muxffmpegMetadata (): Promise { - if (!this.isDownloaded()) throw new Error(`Cannot mux ffmpeg metadata for ${this.title} as its not downloaded. Expected: ${this.expectedSize}, Got: ${await this.downloadedBytes()} bytes...`); - await new Promise((resolve, reject) => execFile( - "./db/ffmpeg", - [ - "-i", - this.filePath, - "-metadata", - `title=${this.title}`, - "-metadata", - `AUTHOR=${this.channel.title}`, - "-metadata", - `YEAR=${this.releaseDate}`, - "-metadata", - `description=${this.description}`, - "-metadata", - `synopsis=${this.description}`, - "-c:a", - "copy", - "-c:v", - "copy", - `${this.filePath}.mp4` - ], (error, stdout) => { - if (error !== null) reject(error); - else resolve(stdout); - } - )); + public async muxffmpegMetadata(): Promise { + if (!this.isDownloaded()) + throw new Error( + `Cannot mux ffmpeg metadata for ${this.title} as its not downloaded. Expected: ${this.expectedSize}, Got: ${await this.downloadedBytes()} bytes...` + ); + await new Promise((resolve, reject) => + execFile( + './db/ffmpeg', + [ + '-i', + `${this.filePath}.partial`, + '-metadata', + `title=${this.title}`, + '-metadata', + `AUTHOR=${this.channel.title}`, + '-metadata', + `YEAR=${this.releaseDate}`, + '-metadata', + `date=${this.releaseDate.getFullYear().toString() + nPad(this.releaseDate.getMonth() + 1) + nPad(this.releaseDate.getDate())}`, + '-metadata', + `description=${htmlToText(this.description)}`, + '-metadata', + `synopsis=${htmlToText(this.description)}`, + '-c:a', + 'copy', + '-c:v', + 'copy', + `${this.filePath}.mp4`, + ], + (error, stdout) => { + if (error !== null) reject(error); + else resolve(stdout); + } + ) + ); this.expectedSize = await this.muxedBytes(); await this.markCompleted(); - await fs.unlink(this.filePath); + await fs.unlink(`${this.filePath}.partial`); // Set the files update time to when the video was released await fs.utimes(`${this.filePath}.mp4`, new Date(), this.releaseDate); } -} \ No newline at end of file + + public async postProcessingCommand(): Promise { + const result = await exec(this.formatString(settings.postProcessingCommand)); + if (result.stderr !== undefined) throw new Error(result.stderr); + } +} diff --git a/src/lib/defaults.ts b/src/lib/defaults.ts index d9868d7..c2c1369 100644 --- a/src/lib/defaults.ts +++ b/src/lib/defaults.ts @@ -1,119 +1,148 @@ -import { Resolutions, SubChannels, Settings, Args } from "./types"; +import { Resolutions, SubChannels, Settings, Args } from './types'; -export const defaultResoulutions: Resolutions = ["360", "720", "1080", "2160"]; +export const defaultResoulutions: Resolutions = ['360', '720', '1080', '2160']; export const defaultSubChannels: { [key: string]: SubChannels } = { - "Tech Deals": { + 'Tech Deals': { _default: { - title: "Teach Deals", + title: 'Teach Deals', skip: false, identifiers: false, - consoleColor: "\u001b[38;5;10m" - } + consoleColor: '\u001b[38;5;10m', + }, }, - "BitWit Ultr": { + 'BitWit Ultr': { _default: { - title: "BitWit Ultr", + title: 'BitWit Ultr', skip: false, identifiers: false, - consoleColor: "\u001b[38;5;105m" - } + consoleColor: '\u001b[38;5;105m', + }, }, - "Linus Tech Tips": { + 'Linus Tech Tips': { _default: { - title: "Linus Tech Tips", + title: 'Linus Tech Tips', skip: false, identifiers: false, - consoleColor: "\u001b[38;5;208m" + consoleColor: '\u001b[38;5;208m', + }, + 'Mac Address': { + title: 'Mac Address', + skip: false, + identifiers: [ + { + check: 'MA: ', + type: 'title', + }, + ], + consoleColor: '\u001b[38;5;189m', }, - "Mac Address": { - title: "Mac Address", + 'Floatplane Exclusive': { + title: 'Floatplane Exclusive', skip: false, - identifiers: [{ - "check": "MA: ", - "type": "title" - }], - consoleColor: "\u001b[38;5;189m" + identifiers: [ + { + check: 'FP Exclusive: ', + type: 'title', + }, + ], + consoleColor: '\u001b[38;5;200m', }, - "Floatplane Exclusive": { - title: "Floatplane Exclusive", + TalkLinked: { + title: 'TalkLinked', skip: false, - identifiers: [{ - check: "FP Exclusive: ", - type: "title", - }], - consoleColor: "\u001b[38;5;200m" + identifiers: [ + { + check: 'talklinked', + type: 'title', + }, + ], + consoleColor: '\u001b[36m', }, - "TalkLinked": { - title: "TalkLinked", + TechLinked: { + title: 'TechLinked', skip: false, - identifiers: [{ - check: "talklinked", - type: "title", - }], - consoleColor: "\u001b[36m" + identifiers: [ + { + check: 'TL: ', + type: 'title', + }, + ], + consoleColor: '\u001b[38;5;14m', }, - "TechLinked": { - title: "TechLinked", + 'TechLinked Shorts': { + title: 'TechLinked Shorts', skip: false, - identifiers: [{ - check: "TL: ", - type: "title", - }], - consoleColor: "\u001b[38;5;14m" + identifiers: [ + { + check: 'TL Short: ', + type: 'title', + }, + ], + consoleColor: '\u001b[38;5;14m', }, - "TechQuickie": { - title: "TechQuickie", + TechQuickie: { + title: 'TechQuickie', skip: false, - identifiers: [{ - check: "TQ: ", - type: "title", - }], - consoleColor: "\u001b[38;5;153m" + identifiers: [ + { + check: 'TQ: ', + type: 'title', + }, + ], + consoleColor: '\u001b[38;5;153m', }, - "Carpool Critics": { - title: "Carpool Critics", + 'Carpool Critics': { + title: 'Carpool Critics', skip: false, - identifiers: [{ - check: "CC: ", - type: "title", - }] + identifiers: [ + { + check: 'CC: ', + type: 'title', + }, + ], }, - "ShortCircuit": { - title: "ShortCircuit", + ShortCircuit: { + title: 'ShortCircuit', skip: false, - identifiers: [{ - check: "SC: ", - type: "title", - }], + identifiers: [ + { + check: 'SC: ', + type: 'title', + }, + ], }, - "ChannelSuperFun": { - title: "ChannelSuperFun", + ChannelSuperFun: { + title: 'ChannelSuperFun', skip: false, - identifiers: [{ - check: "CSF: ", - type: "title" - }], - consoleColor: "\u001b[38;5;220m" + identifiers: [ + { + check: 'CSF: ', + type: 'title', + }, + ], + consoleColor: '\u001b[38;5;220m', }, - "LMG Livestream VODs": { - title: "LMG Livestream VODs", + 'LMG Livestream VODs': { + title: 'LMG Livestream VODs', skip: false, - identifiers: [{ - check: "Livestream VOD – ", - type: "title" - }], - consoleColor: "\u001b[38;5;208m" - } - } + identifiers: [ + { + check: 'Livestream VOD – ', + type: 'title', + }, + ], + consoleColor: '\u001b[38;5;208m', + }, + }, }; export const defaultArgs: Args = { - username: "", - password: "", - token: "", + username: '', + password: '', + token: '', headless: false, - plexUsername: "", - plexPassword: "" + plexUsername: '', + plexPassword: '', }; export const defaultSettings: Settings = { @@ -121,26 +150,29 @@ export const defaultSettings: Settings = { downloadThreads: -1, floatplane: { videosToSearch: 5, - videoResolution: "1080", + videoResolution: '1080', waitForNewVideos: true, - _avalibleResolutions: defaultResoulutions + _avalibleResolutions: defaultResoulutions, }, - _filePathFormattingOPTIONS: ["%channelTitle%", "%episodeNumber%", "%videoTitle%", "%year%", "%month%"], - filePathFormatting: "./videos/%channelTitle%/%channelTitle% - S01E%episodeNumber% - %videoTitle%", + _filePathFormattingOPTIONS: ['%channelTitle%', '%episodeNumber%', '%videoTitle%', '%year%', '%month%', '%day%', '%hour%', '%minute%', '%second%'], + filePathFormatting: './videos/%channelTitle%/%channelTitle% - S%year%E%month%%day%%hour%%minute%%second% - %videoTitle%', extras: { + stripSubchannelPrefix: true, downloadArtwork: true, saveNfo: true, }, + artworkSuffix: '', plex: { sectionsToUpdate: [], enabled: false, - token: "", + token: '', }, channelAliases: { - "linus tech tips": "Linus Tech Tips", - "ltt supporter (og)": "Linus Tech Tips", - "ltt supporter (1080p)": "Linus Tech Tips", - "ltt supporter plus": "Linus Tech Tips", + 'linus tech tips': 'Linus Tech Tips', + 'ltt supporter (og)': 'Linus Tech Tips', + 'ltt supporter (1080p)': 'Linus Tech Tips', + 'ltt supporter plus': 'Linus Tech Tips', }, - subscriptions: {} -}; \ No newline at end of file + subscriptions: {}, + postProcessingCommand: '', +}; diff --git a/src/lib/ffbinaries.d.ts b/src/lib/ffbinaries.d.ts index b910c05..e84b5f1 100644 --- a/src/lib/ffbinaries.d.ts +++ b/src/lib/ffbinaries.d.ts @@ -1,16 +1,17 @@ -declare module "ffbinaries" { +declare module 'ffbinaries' { /** * Gets binaries for the platform * It will get the data from ffbinaries, pick the correct files * and save it to the specified directory */ - function downloadBinaries(components: string | Array, opts: { - destination?: string; - platform?: string; - }, callback: ( - err: Error, - result: ffbinariesResult - ) => void): void; + function downloadBinaries( + components: string | Array, + opts: { + destination?: string; + platform?: string; + }, + callback: (err: Error, result: ffbinariesResult) => void + ): void; function detectPlatform(): string; @@ -21,5 +22,5 @@ declare module "ffbinaries" { path: string; status: string; code: string; - }> + }>; } diff --git a/src/lib/helpers.ts b/src/lib/helpers.ts index e16f643..44e0fc5 100644 --- a/src/lib/helpers.ts +++ b/src/lib/helpers.ts @@ -1,102 +1,56 @@ -import db from "@inrixia/db"; +import { downloadBinaries, detectPlatform, getBinaryFilename } from 'ffbinaries'; +import { getEnv, rebuildTypes, recursiveUpdate } from '@inrixia/helpers/object'; +import { defaultArgs, defaultSettings } from './defaults'; +import ARGV from 'process.argv'; +import db from '@inrixia/db'; +import fs from 'fs'; -import { isObject } from "@inrixia/helpers/object"; +import type { Args, PartialArgs, Settings } from './types'; -import type { PartialArgs, Settings } from "./types"; -import { defaultArgs, defaultSettings } from "./defaults"; - -import fs from "fs"; - -import { downloadBinaries, detectPlatform, getBinaryFilename } from "ffbinaries"; - -import ARGV from "process.argv"; - - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const rebuildTypes = (object: O, types: T) => { - for (const key in object) { - if (types[key] === undefined) continue; - switch (typeof types[key]) { - case "number": - (object[key] as number) = +object[key]; - break; - case "string": - object[key] = object[key].toString(); - break; - case "boolean": - (object[key] as boolean) = object[key] === "true"; - break; - default: - rebuildTypes(object[key], types[key]); - break; - } - } - return object; -}; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const recursiveUpdate = (targetObject: any, newObject: any, setUndefined = true, setDefined = false) => { - if (!isObject(targetObject)) throw new Error("targetObject is not an object!"); - if (!isObject(newObject)) throw new Error("newObject is not an object!"); - for (const key in newObject) { - if (targetObject[key] === undefined) { - if (setUndefined) targetObject[key] = newObject[key]; - } else if (setDefined) targetObject[key] = newObject[key]; - else if (isObject(targetObject[key]) && isObject(newObject[key])) recursiveUpdate(targetObject[key], newObject[key]); - } -}; - -/** - * Converts process.env variables into a object - * @example process.env["some_subproperty"] = "hello" - * returns { some: { subProperty: "hello" } } - */ -const getEnv = () => { - // Define our return object env variables are applied to. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const envObject = {} as Record; - // Iterate over env keys - for (const envKey in process.env) { - // Set our reference object to be equal to the envObject to begin with. - let objRef = envObject; - // Break apart the envKey into its keys. Ex some_subProperty = ["some", "subProperty"] - const keys = envKey.split("_"); - // For every key except the last... - for (let i = 0; i < keys.length-1; i++) { - // Set the key on the objRef to a empty object if its undefined. - objRef = objRef[keys[i]] ??= {}; - } - // Set the last key to equal the original value - if (typeof objRef === "object") objRef[keys[keys.length-1]] = process.env[envKey]; - } - return envObject; -}; - -export const settings = db("./db/settings.json", defaultSettings, { pretty: true }); +export const settings = db('./db/settings.json', { template: defaultSettings, pretty: true, forceCreate: true }); recursiveUpdate(settings, defaultSettings); +const argv = ARGV(process.argv.slice(2))({}); +rebuildTypes(argv, { ...defaultSettings, ...defaultArgs }); +recursiveUpdate(settings, argv, { setUndefined: false, setDefined: true }); -const argv = rebuildTypes(ARGV(process.argv.slice(2))({}), { ...defaultSettings, ...defaultArgs }); -recursiveUpdate(settings, argv, false, true); - -const env = rebuildTypes(getEnv(), { ...defaultSettings, ...defaultArgs }); -recursiveUpdate(settings, env, false, true); +const env = getEnv(); +rebuildTypes(env, { ...defaultSettings, ...defaultArgs }); +recursiveUpdate(settings, env, { setUndefined: false, setDefined: true }); export const args = { ...argv, ...env }; -export const fetchFFMPEG = (): Promise => new Promise((resolve, reject) => { - const platform = detectPlatform(); - if (fs.existsSync(`./db/${getBinaryFilename("ffmpeg", platform)}`) === false) { - process.stdout.write("> Ffmpeg binary missing! Downloading... "); - downloadBinaries("ffmpeg", { - destination: "./db/", - platform - }, err => { - if (err !== null) reject(err); - else { - process.stdout.write("\u001b[36mDone!\u001b[0m\n\n"); - resolve(); - } - }); - } else resolve(); -}); \ No newline at end of file +// Override stdout if headless to not include formatting tags +if (args.headless === true) { + const originalStdoutWrite = process.stdout.write.bind(process.stdout); + type StdoutArgs = Parameters; + + process.stdout.write = ((...params: StdoutArgs) => { + // eslint-disable-next-line no-control-regex + if (typeof params[0] === 'string') params[0] = params[0].replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ''); + return originalStdoutWrite(...params); + }) as typeof process.stdout.write; +} + +export const fetchFFMPEG = (): Promise => + new Promise((resolve, reject) => { + const platform = detectPlatform(); + const path = args.headless === true ? './' : './db/'; + if (fs.existsSync(`${path}${getBinaryFilename('ffmpeg', platform)}`) === false) { + process.stdout.write('> Ffmpeg binary missing! Downloading... '); + downloadBinaries( + 'ffmpeg', + { + destination: path, + platform, + }, + (err) => { + if (err !== null) reject(err); + else { + process.stdout.write('\u001b[36mDone!\u001b[0m\n\n'); + resolve(); + } + } + ); + } else resolve(); + }); diff --git a/src/lib/prompts/floatplane.ts b/src/lib/prompts/floatplane.ts index 4980ab3..147fd02 100644 --- a/src/lib/prompts/floatplane.ts +++ b/src/lib/prompts/floatplane.ts @@ -1,45 +1,57 @@ -import { requiredPrompts } from "./helpers.js"; -import prompts from "prompts"; +import { requiredPrompts } from './helpers.js'; +import prompts from 'prompts'; /** * Prompts user for floatplane username * @returns {Promise} Username */ -export const username = async (): Promise => (await requiredPrompts({ - type: "text", - name: "username", - message: "Please enter your floatplane email/username" -})).username; +export const username = async (): Promise => + ( + await requiredPrompts({ + type: 'text', + name: 'username', + message: 'Please enter your floatplane email/username', + }) + ).username; /** * Prompts user for floatplane password * @returns {Promise} Password */ -export const password = async (): Promise => (await requiredPrompts({ - type: "password", - name: "password", - message: "Please enter your floatplane password" -})).password; +export const password = async (): Promise => + ( + await requiredPrompts({ + type: 'password', + name: 'password', + message: 'Please enter your floatplane password', + }) + ).password; /** * Prompts user to set videos to search through for downloads. * @param {number} initial Default value * @returns {Promise} Videos to search through. */ -export const videosToSearch = async (initial: number): Promise => (await prompts({ - type: "number", - name: "videosToSearch", - message: "How many videos back from the latest do you want to search through for ones to download?", - initial, - min: 0 -})).videosToSearch||initial; +export const videosToSearch = async (initial: number): Promise => + ( + await prompts({ + type: 'number', + name: 'videosToSearch', + message: 'How many videos back from the latest do you want to search through for ones to download?', + initial, + min: 0, + }) + ).videosToSearch || initial; /** * Prompts user for floatplane token. * @returns {Promise} Floatplane OAuth Token */ -export const token = async (): Promise => (await requiredPrompts({ - type: "text", - name: "token", - message: "Please enter your floatplane 2Factor authentication token" -})).token; \ No newline at end of file +export const token = async (): Promise => + ( + await requiredPrompts({ + type: 'text', + name: 'token', + message: 'Please enter your floatplane 2Factor authentication token', + }) + ).token; diff --git a/src/lib/prompts/helpers.ts b/src/lib/prompts/helpers.ts index 00d2dc0..cf1c695 100644 --- a/src/lib/prompts/helpers.ts +++ b/src/lib/prompts/helpers.ts @@ -1,4 +1,4 @@ -import prompts from "prompts"; +import prompts from 'prompts'; /** * Prompts a user for input, onCancel tries again `maxDepth` times. @@ -6,11 +6,17 @@ import prompts from "prompts"; * @param maxDepth Maximum times to prompt * @param cancelPrompt String to sent to stdout when user cancels (omitted on last cancel). */ -export const requiredPrompts = (prompt: prompts.PromptObject, maxDepth=2, cancelPrompt="\nAnswering this prompt is required to continue.\n", depth=0): Promise> => new Promise(res => { - if (depth > 0 && depth < maxDepth) process.stdout.write(cancelPrompt); - if (depth >= maxDepth) { - console.log(`\nRequired prompt cancelled ${maxDepth} times! Exiting...`); - process.exit(); - } - prompts(prompt, { onCancel: () => res(requiredPrompts(prompt, maxDepth, cancelPrompt, depth+=1)) }).then(res); -}); \ No newline at end of file +export const requiredPrompts = ( + prompt: prompts.PromptObject, + maxDepth = 2, + cancelPrompt = '\nAnswering this prompt is required to continue.\n', + depth = 0 +): Promise> => + new Promise((res) => { + if (depth > 0 && depth < maxDepth) process.stdout.write(cancelPrompt); + if (depth >= maxDepth) { + console.log(`\nRequired prompt cancelled ${maxDepth} times! Exiting...`); + process.exit(); + } + prompts(prompt, { onCancel: () => res(requiredPrompts(prompt, maxDepth, cancelPrompt, (depth += 1))) }).then(res); + }); diff --git a/src/lib/prompts/index.ts b/src/lib/prompts/index.ts index cfac7d0..d2f0f4e 100644 --- a/src/lib/prompts/index.ts +++ b/src/lib/prompts/index.ts @@ -1,4 +1,4 @@ -export * as settings from "./settings"; -export * as floatplane from "./floatplane"; -export * as plex from "./plex"; -export * from "./prompts"; \ No newline at end of file +export * as settings from './settings'; +export * as floatplane from './floatplane'; +export * as plex from './plex'; +export * from './prompts'; diff --git a/src/lib/prompts/plex.ts b/src/lib/prompts/plex.ts index 3009614..1b591b5 100644 --- a/src/lib/prompts/plex.ts +++ b/src/lib/prompts/plex.ts @@ -1,82 +1,99 @@ -import prompts from "prompts"; -import { requiredPrompts } from "./helpers"; +import prompts from 'prompts'; +import { requiredPrompts } from './helpers'; /** * Prompts user if they want to automatically refresh plex libraries. * @param {boolean} initial Default value * @returns {Promise} True or False */ -export const usePlex = async (initial: boolean): Promise => (await prompts({ - type: "toggle", - name: "usePlex", - message: "Do you want to automatically refresh plex libraries?", - initial, - active: "Yes", - inactive: "No" -})).usePlex; +export const usePlex = async (initial: boolean): Promise => + ( + await prompts({ + type: 'toggle', + name: 'usePlex', + message: 'Do you want to automatically refresh plex libraries?', + initial, + active: 'Yes', + inactive: 'No', + }) + ).usePlex; -import type { Section } from "@ctrl/plex"; -import { PlexSections } from "../types"; +import type { Section } from '@ctrl/plex'; +import { PlexSections } from '../types'; /** * Prompts user to select plex sections to refresh * @param selectedSections Sections already selected * @param avalibleSections Array of avalible plex sections */ -export const sections = async (selectedSections: PlexSections, avalibleSections: Section[]): Promise => (await prompts({ - type: "multiselect", - name: "sections", - message: "Please select the plex section's you want to refresh.", - choices: Object.values(avalibleSections) - .map(avalibleSection => ({ - title: `[${avalibleSection.server.friendlyName}]: ${avalibleSection.title}`, - value: { server: avalibleSection.server.friendlyName, section: avalibleSection.title }, - selected: selectedSections.includes({ server: avalibleSection.server.friendlyName, section: avalibleSection.title }) - })), - hint: "- Space to select. Return to submit" -})).sections||selectedSections; +export const sections = async (selectedSections: PlexSections, avalibleSections: Section[]): Promise => + ( + await prompts({ + type: 'multiselect', + name: 'sections', + message: "Please select the plex section's you want to refresh.", + choices: Object.values(avalibleSections).map((avalibleSection) => ({ + title: `[${avalibleSection.server.friendlyName}]: ${avalibleSection.title}`, + value: { server: avalibleSection.server.friendlyName, section: avalibleSection.title }, + selected: selectedSections.includes({ server: avalibleSection.server.friendlyName, section: avalibleSection.title }), + })), + hint: '- Space to select. Return to submit', + }) + ).sections || selectedSections; /** * Prompts user for their plex username. - * @param {string} initial + * @param {string} initial * @returns {Promise} */ -export const username = async (): Promise => (await requiredPrompts({ - type: "text", - name: "username", - message: "Plex account email/username:", -})).username; +export const username = async (): Promise => + ( + await requiredPrompts({ + type: 'text', + name: 'username', + message: 'Plex account email/username:', + }) + ).username; /** * Prompts user for their plex password. - * @param {string} initial + * @param {string} initial * @returns {Promise} */ -export const password = async (): Promise => (await requiredPrompts({ - type: "password", - name: "password", - message: "Plex account password, (If you have 2factor enabled add your 2factor code at the end):", -})).password; +export const password = async (): Promise => + ( + await requiredPrompts({ + type: 'password', + name: 'password', + message: 'Plex account password, (If you have 2factor enabled add your 2factor code at the end):', + }) + ).password; /** * Prompts user for their plex server hosname. * @param {string} initial * @returns {Promise} */ -export const hostname = async (initial: string): Promise => (await prompts({ - type: "text", - name: "hostname", - message: "Plex server IP/Hostname:", - initial -})).hostname||initial; +export const hostname = async (initial: string): Promise => + ( + await prompts({ + type: 'text', + name: 'hostname', + message: 'Plex server IP/Hostname:', + initial, + }) + ).hostname || initial; /** * Prompts user for their plex server port. - * @param {number} initial + * @param {number} initial * @returns {Promise} */ -export const port = async (initial: number): Promise => (await prompts({ - type: "text", - name: "port", - message: "Plex server port:", - initial -})).port||initial; \ No newline at end of file +export const port = async (initial: number): Promise => + ( + await prompts({ + type: 'text', + name: 'port', + message: 'Plex server port:', + initial, + }) + ).port || initial; diff --git a/src/lib/prompts/prompts.ts b/src/lib/prompts/prompts.ts index dc5bbb7..0ead430 100644 --- a/src/lib/prompts/prompts.ts +++ b/src/lib/prompts/prompts.ts @@ -1,29 +1,35 @@ -import prompts from "prompts"; +import prompts from 'prompts'; /** * Prompts if user wants to save their login details. * @param initial Default value * @returns True or False */ -export const saveLoginDetails = async (initial=false): Promise => (await prompts({ - type: "toggle", - name: "save", - message: "Save your login details?", - initial, - active: "Yes", - inactive: "No" -})).save; +export const saveLoginDetails = async (initial = false): Promise => + ( + await prompts({ + type: 'toggle', + name: 'save', + message: 'Save your login details?', + initial, + active: 'Yes', + inactive: 'No', + }) + ).save; /** * Prompts user if they want to find the closest download server now. * @param initial Default value * @returns True or False */ -export const findClosestServerNow = async (initial=true): Promise => (await prompts({ - type: "toggle", - name: "findBestServer", - message: "Would you like to try find the best download server?", - initial, - active: "Yes", - inactive: "No" -})).findBestServer; \ No newline at end of file +export const findClosestServerNow = async (initial = true): Promise => + ( + await prompts({ + type: 'toggle', + name: 'findBestServer', + message: 'Would you like to try find the best download server?', + initial, + active: 'Yes', + inactive: 'No', + }) + ).findBestServer; diff --git a/src/lib/prompts/settings.ts b/src/lib/prompts/settings.ts index 62b2f7c..8f10474 100644 --- a/src/lib/prompts/settings.ts +++ b/src/lib/prompts/settings.ts @@ -1,33 +1,39 @@ -import prompts from "prompts"; -import type { Extras, Resolution } from "../types"; +import prompts from 'prompts'; +import type { Extras, Resolution } from '../types'; /** * Prompts if user wants to encrypt their authentication details. * @param initial Default value * @returns {Promise} True of False */ -export const encryptAuthDB = async (initial=true): Promise => (await prompts({ - type: "toggle", - name: "crypt", - message: "Encrypt authentication database (recommended)?", - initial, - active: "Yes", - inactive: "No" -})).crypt; +export const encryptAuthDB = async (initial = true): Promise => + ( + await prompts({ + type: 'toggle', + name: 'crypt', + message: 'Encrypt authentication database (recommended)?', + initial, + active: 'Yes', + inactive: 'No', + }) + ).crypt; /** * Prompts if user wants to have auto repeating enabled. * @param {boolean} initial Default value * @returns {Promise} True of False */ -export const repeat = async (initial: boolean): Promise => (await prompts({ - type: "toggle", - name: "repeat", - message: "Auto repeat video fetching?", - initial, - active: "Yes", - inactive: "No" -})).repeat; +export const repeat = async (initial: boolean): Promise => + ( + await prompts({ + type: 'toggle', + name: 'repeat', + message: 'Auto repeat video fetching?', + initial, + active: 'Yes', + inactive: 'No', + }) + ).repeat; /** * Prompts user to set the interval to auto repeat @@ -35,13 +41,15 @@ export const repeat = async (initial: boolean): Promise => (await promp * @returns {Promise} HH:mm:ss */ export const repeatInterval = async (initial: string): Promise => { - const repeatInterval = (await prompts({ - type: "date", - name: "repeatInterval", - message: "Please set the interval to repeat video fetching. HH:mm:ss", - mask: "HH:mm:ss", - initial: new Date(`1999 ${initial}`) - })).repeatInterval; + const repeatInterval = ( + await prompts({ + type: 'date', + name: 'repeatInterval', + message: 'Please set the interval to repeat video fetching. HH:mm:ss', + mask: 'HH:mm:ss', + initial: new Date(`1999 ${initial}`), + }) + ).repeatInterval; return repeatInterval === undefined ? initial : `${repeatInterval.getHours()}:${repeatInterval.getMinutes()}:${repeatInterval.getSeconds()}`; }; @@ -50,25 +58,31 @@ export const repeatInterval = async (initial: string): Promise => { * @param {string} initial Default value * @returns {Promise} Folder path to save videos */ -export const videoFolder = async (initial: string): Promise => (await prompts({ - type: "text", - name: "videoFolder", - message: "What folder do you want to save videos?", - initial -})).videoFolder||initial; +export const videoFolder = async (initial: string): Promise => + ( + await prompts({ + type: 'text', + name: 'videoFolder', + message: 'What folder do you want to save videos?', + initial, + }) + ).videoFolder || initial; /** * Prompts user to set the max number of parallel downloads. * @param {number} initial Default value * @returns {Promise} Max number of parallel downloads */ -export const downloadThreads = async (initial: number): Promise => (await prompts({ - type: "number", - name: "downloadThreads", - message: "What is the number of threads to use for downloads? (-1 for unlimited).", - initial, - min: -1 -})).downloadThreads||initial; +export const downloadThreads = async (initial: number): Promise => + ( + await prompts({ + type: 'number', + name: 'downloadThreads', + message: 'What is the number of threads to use for downloads? (-1 for unlimited).', + initial, + min: -1, + }) + ).downloadThreads || initial; /** * Prompts user for the video resolution they want to download in. @@ -76,13 +90,16 @@ export const downloadThreads = async (initial: number): Promise => (awai * @param {Array} resolutions Avalible resolutions * @returns {Promise} Resolution to use */ -export const videoResolution = async (initial: Resolution, resolutions: Array): Promise => (await prompts({ - type: "select", - name: "resolution", - message: "What resolution would you like to download in?", - choices: resolutions.map(res => ({ title: `${res}p`, value: res, disabled: false })), - initial: resolutions.indexOf(initial) -})).resolution||initial; +export const videoResolution = async (initial: Resolution, resolutions: Array): Promise => + ( + await prompts({ + type: 'select', + name: 'resolution', + message: 'What resolution would you like to download in?', + choices: resolutions.map((res) => ({ title: `${res}p`, value: res, disabled: false })), + initial: resolutions.indexOf(initial), + }) + ).resolution || initial; /** * Prompts user to specify the file formatting to use for saving videos. Options avalible for use are created from `options` @@ -90,36 +107,45 @@ export const videoResolution = async (initial: Resolution, resolutions: Array} options File formatting options avalible * @returns {Promise} File formatting to use */ -export const fileFormatting = async (initial: string, options: Array): Promise => (await prompts({ - type: "text", - name: "fileFormatting", - message: "What format should be used for saving videos? The following values can be used:\n"+options.reduce((str, option) => `${str} - ${option}\n`, ""), - initial -})).fileFormatting||initial; +export const fileFormatting = async (initial: string, options: Array): Promise => + ( + await prompts({ + type: 'text', + name: 'fileFormatting', + message: 'What format should be used for saving videos? The following values can be used:\n' + options.reduce((str, option) => `${str} - ${option}\n`, ''), + initial, + }) + ).fileFormatting || initial; /** * Prompts user to set any extra boolean settings * @param options Object containing extra settings * @returns {Promise>} Keynames of enabled extras */ -export const extras = async (initial: Extras): Promise|undefined> => (await prompts({ - type: "multiselect", - name: "extras", - message: "Enable/Disable Extra Options:", - choices: Object.keys(initial).map(option => ({ title: option, value: option, selected: initial[option] })), - hint: "- Space to select. Return to submit" -})).extras; +export const extras = async (initial: Extras): Promise | undefined> => + ( + await prompts({ + type: 'multiselect', + name: 'extras', + message: 'Enable/Disable Extra Options:', + choices: (Object.keys(initial) as [keyof Extras]).map((option) => ({ title: option, value: option, selected: initial[option] })), + hint: '- Space to select. Return to submit', + }) + ).extras; /** * Proompts user if they want to find the closest download server automatically in the future. * @param {boolean} initial Default value * @returns {Promise} True or False */ -export const autoFindClosestServer = async (initial: boolean): Promise => (await prompts({ - type: "toggle", - name: "bestEdge", - message: "Automatically find the best server in the future?", - initial, - active: "Yes", - inactive: "No" -})).bestEdge; \ No newline at end of file +export const autoFindClosestServer = async (initial: boolean): Promise => + ( + await prompts({ + type: 'toggle', + name: 'bestEdge', + message: 'Automatically find the best server in the future?', + initial, + active: 'Yes', + inactive: 'No', + }) + ).bestEdge; diff --git a/src/lib/types.ts b/src/lib/types.ts index 3feb68e..b2477d1 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,16 +1,16 @@ +export type Resolutions = ['360', '720', '1080', '2160']; -export type Resolution = "360" | "720" | "1080" | "2160"; -export type Resolutions = ["360", "720", "1080", "2160"]; - -import type { BlogPost } from "floatplane/creator"; +import type { BlogPost } from 'floatplane/creator'; export type ChannelOptions = { title: string; skip: boolean; - identifiers: Array<{ - check: string; - type: keyof BlogPost | "description"; - }> | false; + identifiers: + | Array<{ + check: string; + type: keyof BlogPost | 'description'; + }> + | false; consoleColor?: string; }; @@ -19,21 +19,19 @@ export type SubChannels = { [key: string]: ChannelOptions; }; -export type PlexSections = Array<{ server: string, section: string }>; +export type PlexSections = Array<{ server: string; section: string }>; export type PlexSettings = { - sectionsToUpdate: PlexSections, + sectionsToUpdate: PlexSections; enabled: boolean; token: string; -} - -export type Extras = { [key: string]: boolean } +}; export type SubscriptionSettings = { creatorId: string; plan: string; skip: boolean; channels: SubChannels; -} +}; export type Args = { username: string; @@ -42,10 +40,32 @@ export type Args = { headless: boolean; plexUsername: string; plexPassword: string; -} +}; export type PartialArgs = Partial; +export type Extras = { + stripSubchannelPrefix: boolean; + downloadArtwork: boolean; + saveNfo: boolean; +}; + +export type ValueOf = T[keyof T]; + +export type FilePathFormattingOptions = { + '%channelTitle%': string; + '%episodeNumber%': string; + '%year%': string; + '%month%': string; + '%day%': string; + '%hour%': string; + '%minute%': string; + '%second%': string; + '%videoTitle%': string; +}; + +export type Resolution = ValueOf; + export type Settings = { runQuickstartPrompts: boolean; downloadThreads: number; @@ -55,14 +75,16 @@ export type Settings = { waitForNewVideos: boolean; _avalibleResolutions: Resolutions; }; - _filePathFormattingOPTIONS: string[]; + _filePathFormattingOPTIONS: (keyof FilePathFormattingOptions)[]; filePathFormatting: string; extras: Extras; - channelAliases: { - [key: string]: string + artworkSuffix: string; + channelAliases: { + [key: string]: string; }; subscriptions: { [key: string]: SubscriptionSettings; }; plex: PlexSettings; + postProcessingCommand: string; }; diff --git a/src/logins.ts b/src/logins.ts index 6700d27..777bc60 100644 --- a/src/logins.ts +++ b/src/logins.ts @@ -1,29 +1,32 @@ -import { MyPlexAccount } from "@ctrl/plex"; - -import { loopError } from "@inrixia/helpers/object"; - -import { floatplane, plex } from "./lib/prompts"; - -import { args } from "./lib/helpers"; - -import { fApi } from "./lib/FloatplaneAPI"; +import { loopError } from '@inrixia/helpers/object'; +import { floatplane, plex } from './lib/prompts'; +import { fApi } from './lib/FloatplaneAPI'; +import { MyPlexAccount } from '@ctrl/plex'; +import { args } from './lib/helpers'; export const loginFloatplane = async (): Promise => { let loginResponse; if (args.headless === true) { - if (args.username === undefined || args.password === undefined) throw new Error("Need floatplane username/password to login. Please pass them as --username=\"\" --password=\"\" or enviroment variables!"); + if (args.username === undefined || args.password === undefined) + throw new Error('Need floatplane username/password to login. Please pass them as --username="" --password="" or enviroment variables!'); loginResponse = await fApi.auth.login(args.username, args.password); if (loginResponse.needs2FA) { - if (args.token === undefined) throw new Error("Need floatplane 2Factor token to login. Please pass it as --token=\"\" or an enviroment variable!"); + if (args.token === undefined) throw new Error('Need floatplane 2Factor token to login. Please pass it as --token="" or an enviroment variable!'); loginResponse = await fApi.auth.factor(args.token); } } else { - loginResponse = await loopError(async () => fApi.auth.login(await floatplane.username(), await floatplane.password()), async err => console.error(`\nLooks like those login details didnt work, Please try again... ${err}`)); + loginResponse = await loopError( + async () => fApi.auth.login(await floatplane.username(), await floatplane.password()), + async (err) => console.error(`\nLooks like those login details didnt work, Please try again... ${err}`) + ); if (loginResponse.needs2FA) { - console.log("Looks like you have 2Factor authentication enabled. Nice!\n"); - loginResponse = await loopError(async () => fApi.auth.factor(await floatplane.token()), async err => console.error(`\nLooks like that 2Factor token didnt work, Please try again... ${err}`)); + console.log('Looks like you have 2Factor authentication enabled. Nice!\n'); + loginResponse = await loopError( + async () => fApi.auth.factor(await floatplane.token()), + async (err) => console.error(`\nLooks like that 2Factor token didnt work, Please try again... ${err}`) + ); } } console.log(`\nSigned in as \u001b[36m${loginResponse.user.username}\u001b[0m!\n`); @@ -32,18 +35,19 @@ export const loginFloatplane = async (): Promise => { export const loginPlex = async (): Promise => { let plexToken: string; if (args.headless === true) { - if (args.plexUsername === undefined || args.plexPassword === undefined) throw new Error("Need plex username/password to login. Please pass them as --plexUsername=\"\" --plexPassword=\"\" or enviroment variables!"); - plexToken = await loopError( - async () => (await new MyPlexAccount(undefined, args.plexUsername, args.plexPassword).connect()).token, - async err => console.error(`\nLooks like those login details didnt work, Please try again... ${err}`) - ) as string; + if (args.plexUsername === undefined || args.plexPassword === undefined) + throw new Error('Need plex username/password to login. Please pass them as --plexUsername="" --plexPassword="" or enviroment variables!'); + plexToken = (await loopError( + async () => (await new MyPlexAccount(undefined, args.plexUsername, args.plexPassword).connect()).token, + async (err) => console.error(`\nLooks like those login details didnt work, Please try again... ${err}`) + )) as string; } else { - console.log("\n> Please enter your plex details. (Username and Password is not saved, only used to generate a token.)"); - plexToken = await loopError( - async () => (await new MyPlexAccount(undefined, await plex.username(), await plex.password()).connect()).token, - async err => console.error(`\nLooks like those login details didnt work, Please try again... ${err}`) - ) as string; - console.log(`> Fetched plex token: \u001b[36m${plexToken}\u001b[0m!\n`); + console.log('\n> Please enter your plex details. (Username and Password is not saved, only used to generate a token.)'); + plexToken = (await loopError( + async () => (await new MyPlexAccount(undefined, await plex.username(), await plex.password()).connect()).token, + async (err) => console.error(`\nLooks like those login details didnt work, Please try again... ${err}`) + )) as string; + console.log(`> Fetched plex token: \u001b[36m${plexToken}\u001b[0m!\n`); } return plexToken; -}; \ No newline at end of file +}; diff --git a/src/quickStart.ts b/src/quickStart.ts index 61a5007..298ccc9 100644 --- a/src/quickStart.ts +++ b/src/quickStart.ts @@ -1,43 +1,49 @@ -import * as prompts from "./lib/prompts"; -import { defaultResoulutions } from "./lib/defaults"; -import { loginFloatplane, loginPlex } from "./logins"; -import { MyPlexAccount } from "@ctrl/plex"; +import { loginFloatplane, loginPlex } from './logins'; +import { defaultResoulutions } from './lib/defaults'; +import { args, settings } from './lib/helpers'; +import { MyPlexAccount } from '@ctrl/plex'; +import { fApi } from './lib/FloatplaneAPI'; +import * as prompts from './lib/prompts'; -import { args, settings } from "./lib/helpers"; -import { fApi } from "./lib/FloatplaneAPI"; +import type { Extras } from './lib/types'; export const promptPlexSections = async (): Promise => { - const plexApi = await (new MyPlexAccount(undefined, undefined, undefined, settings.plex.token).connect()); - const servers = (await plexApi.resources()).filter(resource => resource.provides.split(",").indexOf("server") !== -1); - const serverSections = await Promise.all(servers.map(async server => { - const connectedServer = await server.connect(); - const library = await connectedServer.library(); - return (await library.sections()).filter(section => section.type === "show"); - })); - settings.plex.sectionsToUpdate = await prompts.plex.sections(settings.plex.sectionsToUpdate, serverSections.flatMap(sections => sections)); + const plexApi = await new MyPlexAccount(undefined, undefined, undefined, settings.plex.token).connect(); + const servers = (await plexApi.resources()).filter((resource) => resource.provides.split(',').indexOf('server') !== -1); + const serverSections = await Promise.all( + servers.map(async (server) => { + const connectedServer = await server.connect(); + const library = await connectedServer.library(); + return (await library.sections()).filter((section) => section.type === 'show'); + }) + ); + settings.plex.sectionsToUpdate = await prompts.plex.sections( + settings.plex.sectionsToUpdate, + serverSections.flatMap((sections) => sections) + ); if (settings.plex.sectionsToUpdate.length === 0) { - console.log("No sectionsToUpdate in config! Disabling plex integration...\n"); + console.log('No sectionsToUpdate in config! Disabling plex integration...\n'); settings.plex.enabled = false; } }; export const validatePlexSettings = async (): Promise => { if (settings.plex.enabled) { - if (settings.plex.token === "") { - console.log("Missing plex token!"); + if (settings.plex.token === '') { + console.log('Missing plex token!'); settings.plex.token = await loginPlex(); } if (settings.plex.sectionsToUpdate.length === 0) { - console.log("No plex sections specified to update!"); + console.log('No plex sections specified to update!'); await promptPlexSections(); } } }; export const quickStart = async (): Promise => { - console.log("Welcome to Floatplane Downloader! Thanks for checking it out <3."); - console.log("According to your settings.json this is your first launch! So lets go through the basic setup...\n"); - console.log("\n== \u001b[38;5;208mGeneral\u001b[0m ==\n"); + console.log('Welcome to Floatplane Downloader! Thanks for checking it out <3.'); + console.log('According to your settings.json this is your first launch! So lets go through the basic setup...\n'); + console.log('\n== \u001b[38;5;208mGeneral\u001b[0m ==\n'); settings.floatplane.videosToSearch = await prompts.floatplane.videosToSearch(settings.floatplane.videosToSearch); settings.downloadThreads = await prompts.settings.downloadThreads(settings.downloadThreads); @@ -46,20 +52,20 @@ export const quickStart = async (): Promise => { const extras = await prompts.settings.extras(settings.extras); if (extras !== undefined) { - for (const extra in settings.extras) settings.extras[extra] = extras.indexOf(extra) > -1 ? true : false; + for (const extra in settings.extras) settings.extras[extra as keyof Extras] = extras.indexOf(extra) > -1 ? true : false; } - console.log("\n== \u001b[38;5;208mFloatplane\u001b[0m ==\n"); - console.log("Next we are going to login to floatplane..."); + console.log('\n== \u001b[38;5;208mFloatplane\u001b[0m ==\n'); + console.log('Next we are going to login to floatplane...'); // Dont re-prompt for credentials if we are already logged in - if (await fApi.isAuthenticated() !== true) await loginFloatplane(); - else console.log("Already logged in!"); + if ((await fApi.isAuthenticated()) !== true) await loginFloatplane(); + else console.log('Already logged in!'); - console.log("\n== \u001b[38;5;208mPlex\u001b[0m ==\n"); + console.log('\n== \u001b[38;5;208mPlex\u001b[0m ==\n'); settings.plex.enabled = await prompts.plex.usePlex(settings.plex.enabled); if (settings.plex.enabled) { - if (settings.plex.token === "") settings.plex.token = await loginPlex(); + if (settings.plex.token === '') settings.plex.token = await loginPlex(); if (args.headless !== true) await promptPlexSections(); } - console.log("\n== \u001b[36mAll Setup!\u001b[0m ==\n"); -}; \ No newline at end of file + console.log('\n== \u001b[36mAll Setup!\u001b[0m ==\n'); +}; diff --git a/src/subscriptionFetching.ts b/src/subscriptionFetching.ts index 8103a71..c56bbc2 100644 --- a/src/subscriptionFetching.ts +++ b/src/subscriptionFetching.ts @@ -1,30 +1,31 @@ -import { settings } from "./lib/helpers"; -import { defaultSubChannels } from "./lib/defaults"; -import Subscription from "./lib/Subscription"; +import { defaultSubChannels } from './lib/defaults'; +import Subscription from './lib/Subscription'; +import { fApi } from './lib/FloatplaneAPI'; +import { settings } from './lib/helpers'; -import { fApi } from "./lib/FloatplaneAPI"; - -export const fetchSubscriptions = async (): Promise => (await fApi.user.subscriptions()) - .map(subscription => { - // Add the subscription to settings if it doesnt exist - const titleAlias = settings.channelAliases[subscription.plan.title.toLowerCase()]||subscription.plan.title; - settings.subscriptions[subscription.creator] ??= { - creatorId: subscription.creator, - plan: subscription.plan.title, - skip: false, - channels: defaultSubChannels[titleAlias] - }; - // Make sure that new subchannels from defaults are added to settings - settings.subscriptions[subscription.creator].channels = { - ...defaultSubChannels[titleAlias], - ...settings.subscriptions[subscription.creator].channels - }; - // If no defaultSubChannels have been created for this subscription make sure the _default channel exists regardless - if (settings.subscriptions[subscription.creator].channels._default === undefined) settings.subscriptions[subscription.creator].channels._default = { - title: titleAlias, - skip: false, - identifiers: false - }; - return new Subscription(settings.subscriptions[subscription.creator]); - }) - .filter(subscription => settings.subscriptions[subscription.creatorId].skip !== true); \ No newline at end of file +export const fetchSubscriptions = async (): Promise => + (await fApi.user.subscriptions()) + .map((subscription) => { + // Add the subscription to settings if it doesnt exist + const titleAlias = settings.channelAliases[subscription.plan.title.toLowerCase()] || subscription.plan.title; + settings.subscriptions[subscription.creator] ??= { + creatorId: subscription.creator, + plan: subscription.plan.title, + skip: false, + channels: defaultSubChannels[titleAlias], + }; + // Make sure that new subchannels from defaults are added to settings + settings.subscriptions[subscription.creator].channels = { + ...defaultSubChannels[titleAlias], + ...settings.subscriptions[subscription.creator].channels, + }; + // If no defaultSubChannels have been created for this subscription make sure the _default channel exists regardless + if (settings.subscriptions[subscription.creator].channels._default === undefined) + settings.subscriptions[subscription.creator].channels._default = { + title: titleAlias, + skip: false, + identifiers: false, + }; + return new Subscription(settings.subscriptions[subscription.creator]); + }) + .filter((subscription) => settings.subscriptions[subscription.creatorId].skip !== true); diff --git a/src/test.ts b/src/test.ts new file mode 100644 index 0000000..05f86a1 --- /dev/null +++ b/src/test.ts @@ -0,0 +1,65 @@ +import type { BlogPost } from 'floatplane/creator'; +import { settings } from './lib/helpers'; +import db from '@inrixia/db'; + +export const fHistory = db<{ + '59f94c0bdd241b70349eb72b': BlogPost[]; +}>('./db/fHistory.json', { pretty: false, forceCreate: false }); + +// (async () => { +// const subscriptions = await fApi.user.subscriptions(); +// console.log(subscriptions[0].plan); +// const creatorVideos = []; +// let i = 0; +// for await (const video of fApi.creator.blogPostsIterable(subscriptions[0].creator)) { +// console.log(i++); +// creatorVideos.push(video); +// } +// fHistory[subscriptions[0].creator] = creatorVideos; +// })().catch(console.error); + +const lttVideos = [...Object.values(fHistory)[0]]; + +const dates: Record = {}; + +const channels = Object.values(settings.subscriptions['59f94c0bdd241b70349eb72b'].channels); + +const lastVideoPostedDate = lttVideos[0].releaseDate; + +const postTimeDiff = 0; + +for (const video of lttVideos) { + // postTimeDiff += ((+new Date(lastVideoPostedDate))-(+new Date(video.releaseDate))); + + // lastVideoPostedDate = video.releaseDate; + let videoChannel = 'LTT'; + + for (const channel of channels) { + // Check if the video belongs to this channel + if (channel.identifiers === false) continue; + for (const identifier of channel.identifiers) { + if (typeof identifier.type !== 'string') + throw new Error( + `Video value for channel identifier type ${video[identifier.type]} on channel ${channel.title} is of type ${typeof video[identifier.type]} not string!` + ); + else { + // Description is named text on videos, kept description for ease of use for users but have to change it here... + const identifierType = identifier.type === 'description' ? 'text' : identifier.type; + if ((video[identifierType] as string).toLowerCase().indexOf(identifier.check.toLowerCase()) !== -1) { + videoChannel = channel.title; + } + } + } + } + const releaseDate = new Date(video.releaseDate); + const YEAR = releaseDate.getFullYear(); + const MONTH = releaseDate.getMonth() + 1; // If the month is less than 10 pad it with a 0 + const DAY = releaseDate.getDate(); // If the month is less than 10 pad it with a 0 + const HOUR = releaseDate.getHours(); + const MINUTE = releaseDate.getMinutes(); + dates[`${videoChannel}-${YEAR}-${MONTH}-${DAY}-${HOUR}-${MINUTE}`] ??= []; + dates[`${videoChannel}-${YEAR}-${MONTH}-${DAY}-${HOUR}-${MINUTE}`].push([video.title, releaseDate]); +} +console.log(Object.values(dates).filter((date) => date.length > 2)); + +// console.log(postTimeDiff/lttVideos.length/1000/60/60); diff --git a/wiki/docker.md b/wiki/docker.md new file mode 100644 index 0000000..edc5212 --- /dev/null +++ b/wiki/docker.md @@ -0,0 +1,72 @@ +# Docker Wiki + +### Tags: + +- `:latest` + - Latest release version of the downloader +- `:dev` - Image in sync with `dev` branch on github, bleeding edge changes that **will likely break everything**. +
+ +## Quickstart: + +There is a interactive series of console prompts to help you setup the downloader and login. If you dont want to or cannot work with a interactive terminal please skip down to **Enviroment Variables** + + $ docker run -it \ + -v [path]:/fp/db \ + -v [path]:/fp/videos \ + -e headless="true" \ + -e runQuickstartPrompts=true \ + inrix/floatplane-downloader + +- **[path]** should be replaced with a directory on your machine to hold persistent data. +- Setting the Quickstart environment variable to true will create an interactive terminal to walk you through setting up the downloader. + +**After going through the Quickstart, run without quickstart prompts:** + + $ docker run \ + -v [path]:/fp/db \ + -v [path]:/fp/videos \ + -e headless="true" \ + inrixia/floatplane-downloader + +
+ +## Environment Variables: + +Setting environment variables allows you to pass in your login details, removing the need to use the quickstart prompts to login/setup the downloader. + +**For login:** + + $ docker run \ + -v [path]:/fp/db \ + -v [path]:/fp/videos \ + -e headless="true" \ + -e username="YourUsernameHere" \ + -e password="YourPasswordHere" \ + -e token="Your2FactorCodeHere" \ + inrix/floatplane-downloader + +**For login + plex:** + + $ docker run \ + -v [path]:/fp/db \ + -v [path]:/fp/videos \ + -e headless="true" + -e username="YourUsernameHere" \ + -e password="YourPasswordHere" \ + -e plexUsername="YourPlexUsernameHere" \ + -e plexPassword="YourPexPasswordHere2FactorCodeHereIfYouHaveOne" \ + inrix/floatplane-downloader + +You can also use enviroment variables to overwrite/set config values, though the config is persisted under db/config.json.
+To do this you must take the key for the setting in the settings.json and write it with the dots **.** replaced with underscores **\_** you can see an example for the setting `floatplane.videoResolution` below: + +**For settings:** + + $ docker run \ + -v [path]:/fp/db \ + -v [path]:/fp/videos \ + -e headless="true" \ + -e floatplane_videoResolution="1080" \ + -e plex_token="ThisRemovesTheNeedForPassingUsername/Password" \ + inrix/floatplane-downloader diff --git a/wiki/plex.md b/wiki/plex.md index c8638ef..e767346 100644 --- a/wiki/plex.md +++ b/wiki/plex.md @@ -1,17 +1,21 @@ # Plex Library Setup Guide + This assumes you have a **[Plex](https://www.plex.tv/)** sever. ## Create a Library: + Create a plex library (**TV Series**) for Floatplane and set these settings for it:
**Make sure you also add the root folder the downloader is saving videos in to the library so it can find videos** ![https://gyazo.com/4ea679c43fb978135b021a57c7cd39d4](https://i.gyazo.com/4ea679c43fb978135b021a57c7cd39d4.png) -### Posters: +### Posters: + There is a collection of posters you can download, you can find them **[Here](https://github.com/Inrixia/Floatplane-Downloader/tree/master/artwork)**.
Use these to set the artwork for each channel in plex if you dont want it to be blank. ![image](https://user-images.githubusercontent.com/6373693/115113172-142cc800-9fdd-11eb-985a-c5a21bde48b0.png) ### Enable Library Refresh + If you want plex to automatically update as soon as new videos are downloaded you can add your Floatplane library to the plex section of your settings.json...
The easiest way to do this is by setting `runQuickStartPrompts` to **true** and following the prompts to login and setup plex library refreshing.
![image](https://user-images.githubusercontent.com/6373693/115113288-b187fc00-9fdd-11eb-8984-99bf6509a671.png)
@@ -24,5 +28,5 @@ More details can be found on the specific settings at the **[Settings Wiki](http If you dont want to watch the Floatplane content in the plex player and just want to use it for browsing the library. Then you can install **[This Awesome Script](https://github.com/Kayomani/PlexExternalPlayer)** to let you open the folder media is in and auto play plex files in your default media player. -More info on its install is at the above link.
+More info on its install is at the above link.
**Important Note** for chrome users: https://github.com/Kayomani/PlexExternalPlayer/issues/16 diff --git a/wiki/settings.md b/wiki/settings.md index da8f98d..0ca0d0c 100644 --- a/wiki/settings.md +++ b/wiki/settings.md @@ -1,88 +1,149 @@ -# settings.json Wiki +# Settings Wiki + ![image](https://user-images.githubusercontent.com/6373693/115116213-953f8b80-9fec-11eb-9633-08518331aa27.png)
Defaults for any setting is defined in the **[Defaults File](https://github.com/Inrixia/Floatplane-Downloader/blob/master/src/lib/defaults.ts)**.
+**You can find settings under /db/setttings.json**
+Note: Settings can be set in settings.json, using environment variables or passed in as arguments. +For more info on how this work see the section in environment variables in the **[Docker Wiki](https://github.com/Inrixia/Floatplane-Downloader/blob/master/wiki/docker.md)**

**runQuickstartPrompts**:
Setting this to true will cause the quickStartPrompts to run on startup. + ```ts "runQuickstartPrompts": false ``` +
**downloadThreads**:
Sets the maximum amount of downloads that can run concurrently. Default's to -1 which is unlimited, 2 would mean only 2 videos download at once.
+ ```json "downloadThreads": -1 ``` +
## Floatplane **videosToSearch**:
Number of videos to search through when looking for undownloaded videos **per subscription**.
+ ```json "floatplane": { "videosToSearch": 5 } ``` +
**videoResolution**:
Resolution to download the videos in. See `_avalibleResolutions` for options.
-```json + +```json "floatplane": { "videoResolution": 1080 } ``` +
**waitForNewVideos**:
Controls if the downloader should wait for new videos to download after finishing or just exit
+ ```json "floatplane": { "waitForNewVideos": true } ``` +
**filePathFormatting**:
This defined the path/filename formatting for downloaded videos...
You can refer to `_filePathFormattingOPTIONS` for options on what can be used.
Strings surounded by % will be replaced with their respective values.
-```json + +```json "filePathFormatting": "./videos/%channelTitle%/%channelTitle% - S01E%episodeNumber% - %videoTitle%" ``` +
## Extras +**extras.stripSubchannelPrefix**:
+Removes the Subchannel prefix from the video title when a video is sorted into a subchannel.
+For example:
+`TechLinked - SXXEXX - VideoTitle` - **true**
+vs +
+`TechLinked - SXXEXX - TL: VideoTitle` - **false** + +```json +"extras": { + "stripSubchannelPrefix": true +} +``` + +
+ **extras.downloadArtwork**:
Saves video thubnails alongside each video. These are required for nice thumbnails in Plex.
+ ```json "extras": { "downloadArtwork": true } ``` +
**extras.safeNfo**:
Saves video metadata to nfo files alongside each video.
-```json + +```json "extras": { "safeNfo": true } ``` + +
+ +**artworkSuffix**:
+Suffix appended to artwork filename.
+Added for Kodi support as Kodi looks for artwork in the format `VideoName-thumb.png` + +Windows example: + +```json +"artworkSuffix": "echo %videoTitle% > example.txt" +``` + +
+ +**postProcessingCommand**:
+A command to run after each video has sucessfully downloaded.
+You can refer to `_filePathFormattingOPTIONS` for options on what can be used.
+Strings surounded by % will be replaced with their respective values.
+ +```json +"postProcessingCommand": "" +``` +
## Plex + Use **quickstartPrompts** to easily set plex settings. **plex.sectionsToUpdate**:
Array of sections to update on refresh.
Each "section" is a object containing the name of the section and the server it belongs to.
-```json + +```json "plex": { "sectionsToUpdate": [ { @@ -96,20 +157,24 @@ Each "section" is a object containing the name of the section and the server it ] } ``` +
**plex.token**:
Plex token generated from your login details for updating remote servers.
-```json + +```json "plex": { - "token": "xM__2bulgyDf_wulgyE5owodds" + "token": "xM__2bulgyDf_wulgyE5owodds" } ``` +
**channelAliases**:
Array of alias's used to convert subscription names to nice channel names.
-```json + +```json "channelAliases": { "linus tech tips": "Linus Tech Tips", "ltt supporter (og)": "Linus Tech Tips", @@ -117,9 +182,11 @@ Array of alias's used to convert subscription names to nice channel names.
"ltt supporter plus": "Linus Tech Tips" } ``` +
## Subscriptions: + All the Floatplane creators you are subscribed to.
![image](https://user-images.githubusercontent.com/6373693/115116013-86a4a480-9feb-11eb-828a-fe4fa8ba5cf9.png)
At the creator level you can see the `creatorId` and `plan`. You can also choose to `skip` a creator and not download videos from them.
@@ -142,6 +209,7 @@ An Identifier contains two entries `check` and `type`.

For example: + ```json "Floatplane Exclusive": { "title": "Floatplane Exclusive", @@ -155,11 +223,13 @@ For example: "consoleColor": "\u001b[38;5;200m" } ``` + This is a channel named "Floatplane Exclusive".
Videos that have "FP Exclusive: " in their title will be sorted into this channel.

A few more notes regarding channels:
+ - First come first served, the first channel a video matches to is what it goes into, channels are checked top to bottom in the config. Videos cannot be sorted into multiple channels. - You can have multiple identifiers per channel to allow for more accurate matching. - The `check` string is removed from the video's title if the `type`is equal to "title".