diff --git a/.eslintrc.js b/.eslintrc.js index 1ecdbb2..c303aaa 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,5 +22,6 @@ module.exports = { ], 'no-restricted-syntax': 'off', 'no-await-in-loop': 'off', + 'no-continue': 'off', }, }; diff --git a/.github/workflows/node.js.yml b/.github/workflows/tests.yml similarity index 93% rename from .github/workflows/node.js.yml rename to .github/workflows/tests.yml index ffef9de..a99ee0c 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - node-version: [14.x, 15.x, 16.x] + node-version: [16.x, 18.x, 19.x] steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 1945131..713d500 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ node_modules/ -package-lock.json .env diff --git a/.npmignore b/.npmignore index 89a3bb6..74127ac 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,4 @@ node_modules/ .github/ -package-lock.json +tests/ .env diff --git a/examples/AllPrivateIndicators.js b/examples/AllPrivateIndicators.js index 811b09d..c085bec 100644 --- a/examples/AllPrivateIndicators.js +++ b/examples/AllPrivateIndicators.js @@ -1,14 +1,15 @@ const TradingView = require('../main'); -/* - This example creates a chart with - all user's private indicators -*/ +/** + * This example creates a chart with all user's private indicators + */ if (!process.argv[2]) throw Error('Please specify your \'sessionid\' cookie'); +if (!process.argv[3]) throw Error('Please specify your \'signature\' cookie'); const client = new TradingView.Client({ token: process.argv[2], + signature: process.argv[3], }); const chart = new client.Session.Chart(); diff --git a/examples/BuiltInIndicator.js b/examples/BuiltInIndicator.js index ede156b..bc63204 100644 --- a/examples/BuiltInIndicator.js +++ b/examples/BuiltInIndicator.js @@ -1,9 +1,8 @@ const TradingView = require('../main'); -/* - This example tests built-in indicators - like volume-based indicators -*/ +/** + * This example tests built-in indicators like volume-based indicators + */ const volumeProfile = new TradingView.BuiltInIndicator('VbPFixed@tv-basicstudies-139!'); diff --git a/examples/CustomChartType.js b/examples/CustomChartType.js index ab88355..2fb9047 100644 --- a/examples/CustomChartType.js +++ b/examples/CustomChartType.js @@ -1,11 +1,9 @@ const TradingView = require('../main'); -/* - This example creates charts of custom - types such as 'HeikinAshi', 'Renko', - 'LineBreak', 'Kagi', 'PointAndFigure', - and 'Range' with default settings -*/ +/** + * This example creates charts of custom types such as 'HeikinAshi', 'Renko', + * 'LineBreak', 'Kagi', 'PointAndFigure', and 'Range' with default settings. + */ const client = new TradingView.Client({ /* Token is only required if you want to use intraday diff --git a/examples/CustomTimeframe.js b/examples/CustomTimeframe.js index da91436..73c4f75 100644 --- a/examples/CustomTimeframe.js +++ b/examples/CustomTimeframe.js @@ -1,9 +1,8 @@ const TradingView = require('../main'); -/* - This example tests custom - timeframes like 1 second -*/ +/** + * This example tests custom timeframes like 1 second + */ if (!process.argv[2]) throw Error('Please specify your \'sessionid\' cookie'); diff --git a/examples/Errors.js b/examples/Errors.js index fed05cb..8ec7263 100644 --- a/examples/Errors.js +++ b/examples/Errors.js @@ -1,8 +1,8 @@ const TradingView = require('../main'); -/* - This example tests many types of errors -*/ +/** + * This example tests many types of errors + */ const client = new TradingView.Client(); // Creates a websocket client diff --git a/examples/FakeReplayMode.js b/examples/FakeReplayMode.js index 36799a6..441b6d8 100644 --- a/examples/FakeReplayMode.js +++ b/examples/FakeReplayMode.js @@ -1,10 +1,9 @@ const { Client } = require('../main'); -/* - This example tests the fake - replay mode which works in - intraday even with free plan -*/ +/** + * This example tests the fake replay mode which + * works in intraday even with free plan + */ console.log('----- Testing FakeReplayMode: -----'); diff --git a/examples/FromToData.js b/examples/FromToData.js index af47728..cacab04 100644 --- a/examples/FromToData.js +++ b/examples/FromToData.js @@ -1,10 +1,9 @@ const TradingView = require('../main'); -/* - This example tests fetching chart - data of a number of candles before - or after a timestamp -*/ +/** + * This example tests fetching chart data of a number + * of candles before or after a timestamp + */ const client = new TradingView.Client(); diff --git a/examples/GetDrawings.js b/examples/GetDrawings.js index 7222579..e848b70 100644 --- a/examples/GetDrawings.js +++ b/examples/GetDrawings.js @@ -1,9 +1,8 @@ const TradingView = require('../main'); -/* - This example tests the - getDrawings function -*/ +/** + * This example tests the getDrawings function + */ // First parameter must be the layoutID // (if the layout is private) Second parameter must be the sessionid cookie diff --git a/examples/GraphicIndicator.js b/examples/GraphicIndicator.js index 303d64d..d4e926f 100644 --- a/examples/GraphicIndicator.js +++ b/examples/GraphicIndicator.js @@ -1,10 +1,9 @@ const TradingView = require('../main'); -/* - This example tests an indicator that sends - graphic data such as 'lines', 'labels', - 'boxes', 'tables', 'polygons', etc... -*/ +/** + * This example tests an indicator that sends graphic data such + * as 'lines', 'labels', 'boxes', 'tables', 'polygons', etc... + */ const client = new TradingView.Client(); diff --git a/examples/MultipleSyncFetch.js b/examples/MultipleSyncFetch.js index 46c56c6..fde4da7 100644 --- a/examples/MultipleSyncFetch.js +++ b/examples/MultipleSyncFetch.js @@ -1,9 +1,8 @@ const TradingView = require('../main'); -/* - This examples synchronously - fetches data from 3 indicators -*/ +/** + * This examples synchronously fetches data from 3 indicators + */ const client = new TradingView.Client(); const chart = new client.Session.Chart(); diff --git a/examples/PinePermManage.js b/examples/PinePermManage.js index 29db2aa..2bf40c5 100644 --- a/examples/PinePermManage.js +++ b/examples/PinePermManage.js @@ -1,15 +1,15 @@ const { PinePermManager } = require('../main'); -/* - This example creates a pine - permission manager and tests - all the available functions -*/ +/** + * This example creates a pine permission manager + * and tests all the available functions + */ const sessionid = process.argv[2]; -const pineid = process.argv[3]; +const signature = process.argv[3]; +const pineid = process.argv[4]; -const manager = new PinePermManager(sessionid, pineid); +const manager = new PinePermManager(sessionid, signature, pineid); (async () => { console.log('Users:', await manager.getUsers()); diff --git a/examples/ReplayMode.js b/examples/ReplayMode.js index 37a7199..1c28eae 100644 --- a/examples/ReplayMode.js +++ b/examples/ReplayMode.js @@ -1,10 +1,9 @@ const TradingView = require('../main'); -/* - This example tests the real replay - mode by fetching indicator data and - stores it in a 'periods' variable -*/ +/** + * This example tests the real replay mode by fetching + * indicator data and stores it in a 'periods' variable + */ const config = { symbol: 'BINANCE:BTCEUR', diff --git a/examples/Search.js b/examples/Search.js index 4410258..1635177 100644 --- a/examples/Search.js +++ b/examples/Search.js @@ -1,10 +1,9 @@ const TradingView = require('../main'); -/* - This example tests the searching - functions such as 'searchMarket' - and 'searchIndicator' -*/ +/** + * This example tests the searching functions such + * as 'searchMarket' and 'searchIndicator' + */ TradingView.searchMarket('BINANCE:').then((rs) => { console.log('Found Markets:', rs); diff --git a/examples/SimpleChart.js b/examples/SimpleChart.js index 26d15e6..877029d 100644 --- a/examples/SimpleChart.js +++ b/examples/SimpleChart.js @@ -1,8 +1,8 @@ const TradingView = require('../main'); -/* - This example creates a BTCEUR daily chart -*/ +/** + * This example creates a BTCEUR daily chart + */ const client = new TradingView.Client(); // Creates a websocket client diff --git a/examples/UserLogin.js b/examples/UserLogin.js index e9b2f19..1aeb43a 100644 --- a/examples/UserLogin.js +++ b/examples/UserLogin.js @@ -1,9 +1,8 @@ const TradingView = require('../main'); -/* - This example tests the - user login function -*/ +/** + * This example tests the user login function + */ if (!process.argv[2]) throw Error('Please specify your username/email'); if (!process.argv[3]) throw Error('Please specify your password'); @@ -11,6 +10,7 @@ if (!process.argv[3]) throw Error('Please specify your password'); TradingView.loginUser(process.argv[2], process.argv[3], false).then((user) => { console.log('User:', user); console.log('Sessionid:', user.session); + console.log('Signature:', user.signature); }).catch((err) => { console.error('Login error:', err.message); }); diff --git a/package-lock.json b/package-lock.json index 3a9e400..167c2b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,26 @@ { "name": "@mathieuc/tradingview", - "version": "3.3.3", + "version": "3.4.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@mathieuc/tradingview", - "version": "3.3.3", + "version": "3.4.1", "license": "ISC", "dependencies": { + "axios": "^1.5.0", "jszip": "^3.7.1", "ws": "^7.4.3" }, "devDependencies": { "@babel/eslint-parser": "^7.15.7", "@mathieuc/console": "^1.0.1", + "dotenv": "^16.3.1", "eslint": "^7.25.0", "eslint-config-airbnb-base": "^14.2.1", - "eslint-plugin-import": "^2.22.1" + "eslint-plugin-import": "^2.22.1", + "vitest": "^0.31.1" }, "engines": { "node": ">=14.0.0" @@ -527,6 +530,358 @@ "node": ">=6.9.0" } }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -567,12 +922,195 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, "node_modules/@mathieuc/console": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@mathieuc/console/-/console-1.0.1.tgz", "integrity": "sha512-5bSZojJFnxW5rJvipePACJUkIpQBbpQNyImIutEaERO6xkTuMGH3JrW6lxfPddY9vHIbNr+lBjPsCo9Zg3I/eQ==", "dev": true }, + "node_modules/@nodelib/fs.scandir": { + "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, + "optional": true, + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "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, + "optional": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@types/chai": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", + "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "dev": true + }, + "node_modules/@types/chai-subset": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", + "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/node": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.1.tgz", + "integrity": "sha512-DqJociPbZP1lbZ5SQPk4oag6W7AyaGMO6gSfRwq3PWl4PXTwJpRQJhDq4W0kzrg3w6tJ1SwlvGZ5uKFHY13LIg==", + "dev": true + }, + "node_modules/@vitest/expect": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.31.1.tgz", + "integrity": "sha512-BV1LyNvhnX+eNYzJxlHIGPWZpwJFZaCcOIzp2CNG0P+bbetenTupk6EO0LANm4QFt0TTit+yqx7Rxd1qxi/SQA==", + "dev": true, + "dependencies": { + "@vitest/spy": "0.31.1", + "@vitest/utils": "0.31.1", + "chai": "^4.3.7" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.31.1.tgz", + "integrity": "sha512-imWuc82ngOtxdCUpXwtEzZIuc1KMr+VlQ3Ondph45VhWoQWit5yvG/fFcldbnCi8DUuFi+NmNx5ehMUw/cGLUw==", + "dev": true, + "dependencies": { + "@vitest/utils": "0.31.1", + "concordance": "^5.0.4", + "p-limit": "^4.0.0", + "pathe": "^1.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.31.1.tgz", + "integrity": "sha512-L3w5uU9bMe6asrNzJ8WZzN+jUTX4KSgCinEJPXyny0o90fG4FPQMV0OWsq7vrCWfQlAilMjDnOF9nP8lidsJ+g==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.0", + "pathe": "^1.1.0", + "pretty-format": "^27.5.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.31.1.tgz", + "integrity": "sha512-1cTpt2m9mdo3hRLDyCG2hDQvRrePTDgEJBFQQNz1ydHHZy03EiA6EpFxY+7ODaY7vMRCie+WlFZBZ0/dQWyssQ==", + "dev": true, + "dependencies": { + "tinyspy": "^2.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.31.1.tgz", + "integrity": "sha512-+JJ2+rvRPAVxFLNE+WJOMzOjxqYPn7V2hl00uNwid6kquD+UHTa716Z7szfNeZMLnHOHv+fxq1UgLCymvVpE5w==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@vitest/utils": "0.31.1", + "fast-glob": "^3.2.12", + "fflate": "^0.7.4", + "flatted": "^3.2.7", + "pathe": "^1.1.0", + "picocolors": "^1.0.0", + "sirv": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": ">=0.30.1 <1" + } + }, + "node_modules/@vitest/utils": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.31.1.tgz", + "integrity": "sha512-yFyRD5ilwojsZfo3E0BnH72pSVSuLg2356cN1tCEe/0RtDzxTPYwOomIC+eQbot7m6DRy4tPZw+09mB7NkbMmA==", + "dev": true, + "dependencies": { + "concordance": "^5.0.4", + "loupe": "^2.3.6", + "pretty-format": "^27.5.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -594,6 +1132,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -688,6 +1235,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -697,12 +1253,33 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -713,6 +1290,20 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.17.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.2.tgz", @@ -737,6 +1328,15 @@ "url": "https://opencollective.com/browserslist" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -770,6 +1370,24 @@ "url": "https://opencollective.com/browserslist" } }, + "node_modules/chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", @@ -786,6 +1404,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -804,12 +1431,42 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "node_modules/concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "dependencies": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" + } + }, "node_modules/confusing-browser-globals": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", @@ -845,10 +1502,22 @@ "node": ">= 8" } }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -862,6 +1531,18 @@ } } }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -880,6 +1561,14 @@ "node": ">= 0.4" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -892,6 +1581,18 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/electron-to-chromium": { "version": "1.3.857", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.857.tgz", @@ -973,6 +1674,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1321,18 +2059,61 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", + "dev": true, + "optional": true, + "peer": true + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1345,6 +2126,20 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -1371,17 +2166,63 @@ } }, "node_modules/flatted": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz", - "integrity": "sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1404,6 +2245,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -1681,6 +2531,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-number-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", @@ -1747,6 +2608,15 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1809,6 +2679,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "node_modules/jszip": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.8.0.tgz", @@ -1856,6 +2732,18 @@ "node": ">=4" } }, + "node_modules/local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -1869,6 +2757,12 @@ "node": ">=4" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -1887,6 +2781,15 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1899,6 +2802,75 @@ "node": ">=10" } }, + "node_modules/magic-string": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "dependencies": { + "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1917,6 +2889,41 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "node_modules/mlly": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.2.1.tgz", + "integrity": "sha512-1aMEByaWgBPEbWV2BOPEMySRrzl7rIHXmQxam4DM8jVjalTQDjpN2ZKOLUrwyhfZQO7IXHml2StcHMhooDeEEQ==", + "dev": true, + "dependencies": { + "acorn": "^8.8.2", + "pathe": "^1.1.0", + "pkg-types": "^1.0.3", + "ufo": "^1.1.2" + } + }, + "node_modules/mlly/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1930,6 +2937,24 @@ "dev": true, "peer": true }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -2165,6 +3190,41 @@ "node": ">=4" } }, + "node_modules/pathe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz", + "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -2186,6 +3246,17 @@ "node": ">=4" } }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, "node_modules/pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", @@ -2198,6 +3269,34 @@ "node": ">=4" } }, + "node_modules/postcss": { + "version": "8.4.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", + "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2207,6 +3306,32 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2221,6 +3346,11 @@ "node": ">=0.4.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -2230,6 +3360,34 @@ "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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" + } + ], + "optional": true, + "peer": true + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -2314,6 +3472,18 @@ "node": ">=4" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2329,6 +3499,47 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.22.0.tgz", + "integrity": "sha512-imsigcWor5Y/dC0rz2q0bBt9PabcL3TORry2hAa6O6BuMvY71bqHyfReAz5qyAqiQATD1m70qdntqBfBQjVWpQ==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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" + } + ], + "optional": true, + "peer": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2378,6 +3589,28 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "node_modules/sirv": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", + "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -2405,6 +3638,15 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -2443,6 +3685,18 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", + "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==", + "dev": true + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2524,8 +3778,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/supports-color": { - "version": "7.2.0", + "node_modules/strip-literal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.1.tgz", + "integrity": "sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.8.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, @@ -2581,6 +3859,39 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tinybench": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", + "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.5.0.tgz", + "integrity": "sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.0.tgz", + "integrity": "sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -2591,6 +3902,31 @@ "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", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/tsconfig-paths": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", @@ -2614,6 +3950,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -2626,6 +3971,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ufo": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.1.2.tgz", + "integrity": "sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==", + "dev": true + }, "node_modules/unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -2671,6 +4022,176 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/vite": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.8.tgz", + "integrity": "sha512-uYB8PwN7hbMrf4j1xzGDk/lqjsZvCDbt/JC5dyfxc19Pg8kRm14LinK/uq+HSLNswZEoKmweGdtpbnxRtrAXiQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.17.5", + "postcss": "^8.4.23", + "rollup": "^3.21.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.31.1.tgz", + "integrity": "sha512-BajE/IsNQ6JyizPzu9zRgHrBwczkAs0erQf/JRpgTIESpKvNj9/Gd0vxX905klLkb0I0SJVCKbdrl5c6FnqYKA==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.2.0", + "pathe": "^1.1.0", + "picocolors": "^1.0.0", + "vite": "^3.0.0 || ^4.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.31.1.tgz", + "integrity": "sha512-/dOoOgzoFk/5pTvg1E65WVaobknWREN15+HF+0ucudo3dDG/vCZoXTQrjIfEaWvQXmqScwkRodrTbM/ScMpRcQ==", + "dev": true, + "dependencies": { + "@types/chai": "^4.3.5", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "@vitest/expect": "0.31.1", + "@vitest/runner": "0.31.1", + "@vitest/snapshot": "0.31.1", + "@vitest/spy": "0.31.1", + "@vitest/utils": "0.31.1", + "acorn": "^8.8.2", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.7", + "concordance": "^5.0.4", + "debug": "^4.3.4", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.0", + "pathe": "^1.1.0", + "picocolors": "^1.0.0", + "std-env": "^3.3.2", + "strip-literal": "^1.0.1", + "tinybench": "^2.5.0", + "tinypool": "^0.5.0", + "vite": "^3.0.0 || ^4.0.0", + "vite-node": "0.31.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/browser": "*", + "@vitest/ui": "*", + "happy-dom": "*", + "jsdom": "*", + "playwright": "*", + "safaridriver": "*", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2702,6 +4223,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -2742,6 +4279,18 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } }, "dependencies": { @@ -3142,6 +4691,160 @@ "to-fast-properties": "^2.0.0" } }, + "@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "dev": true, + "optional": true + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -3176,12 +4879,161 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, "@mathieuc/console": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@mathieuc/console/-/console-1.0.1.tgz", "integrity": "sha512-5bSZojJFnxW5rJvipePACJUkIpQBbpQNyImIutEaERO6xkTuMGH3JrW6lxfPddY9vHIbNr+lBjPsCo9Zg3I/eQ==", "dev": true }, + "@nodelib/fs.scandir": { + "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, + "optional": true, + "peer": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "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, + "optional": true, + "peer": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", + "dev": true, + "optional": true, + "peer": true + }, + "@types/chai": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", + "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "dev": true + }, + "@types/chai-subset": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", + "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, + "@types/node": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.1.tgz", + "integrity": "sha512-DqJociPbZP1lbZ5SQPk4oag6W7AyaGMO6gSfRwq3PWl4PXTwJpRQJhDq4W0kzrg3w6tJ1SwlvGZ5uKFHY13LIg==", + "dev": true + }, + "@vitest/expect": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.31.1.tgz", + "integrity": "sha512-BV1LyNvhnX+eNYzJxlHIGPWZpwJFZaCcOIzp2CNG0P+bbetenTupk6EO0LANm4QFt0TTit+yqx7Rxd1qxi/SQA==", + "dev": true, + "requires": { + "@vitest/spy": "0.31.1", + "@vitest/utils": "0.31.1", + "chai": "^4.3.7" + } + }, + "@vitest/runner": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.31.1.tgz", + "integrity": "sha512-imWuc82ngOtxdCUpXwtEzZIuc1KMr+VlQ3Ondph45VhWoQWit5yvG/fFcldbnCi8DUuFi+NmNx5ehMUw/cGLUw==", + "dev": true, + "requires": { + "@vitest/utils": "0.31.1", + "concordance": "^5.0.4", + "p-limit": "^4.0.0", + "pathe": "^1.1.0" + }, + "dependencies": { + "p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "requires": { + "yocto-queue": "^1.0.0" + } + } + } + }, + "@vitest/snapshot": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.31.1.tgz", + "integrity": "sha512-L3w5uU9bMe6asrNzJ8WZzN+jUTX4KSgCinEJPXyny0o90fG4FPQMV0OWsq7vrCWfQlAilMjDnOF9nP8lidsJ+g==", + "dev": true, + "requires": { + "magic-string": "^0.30.0", + "pathe": "^1.1.0", + "pretty-format": "^27.5.1" + } + }, + "@vitest/spy": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.31.1.tgz", + "integrity": "sha512-1cTpt2m9mdo3hRLDyCG2hDQvRrePTDgEJBFQQNz1ydHHZy03EiA6EpFxY+7ODaY7vMRCie+WlFZBZ0/dQWyssQ==", + "dev": true, + "requires": { + "tinyspy": "^2.1.0" + } + }, + "@vitest/ui": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.31.1.tgz", + "integrity": "sha512-+JJ2+rvRPAVxFLNE+WJOMzOjxqYPn7V2hl00uNwid6kquD+UHTa716Z7szfNeZMLnHOHv+fxq1UgLCymvVpE5w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@vitest/utils": "0.31.1", + "fast-glob": "^3.2.12", + "fflate": "^0.7.4", + "flatted": "^3.2.7", + "pathe": "^1.1.0", + "picocolors": "^1.0.0", + "sirv": "^2.0.3" + } + }, + "@vitest/utils": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.31.1.tgz", + "integrity": "sha512-yFyRD5ilwojsZfo3E0BnH72pSVSuLg2356cN1tCEe/0RtDzxTPYwOomIC+eQbot7m6DRy4tPZw+09mB7NkbMmA==", + "dev": true, + "requires": { + "concordance": "^5.0.4", + "loupe": "^2.3.6", + "pretty-format": "^27.5.1" + } + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -3195,6 +5047,12 @@ "dev": true, "requires": {} }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3261,18 +5119,45 @@ "es-abstract": "^1.18.0-next.1" } }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3283,6 +5168,17 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "browserslist": { "version": "4.17.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.2.tgz", @@ -3297,6 +5193,12 @@ "node-releases": "^1.1.76" } }, + "cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -3320,6 +5222,21 @@ "dev": true, "peer": true }, + "chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", @@ -3330,6 +5247,12 @@ "supports-color": "^7.1.0" } }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3345,12 +5268,36 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "requires": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + } + }, "confusing-browser-globals": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", @@ -3383,15 +5330,33 @@ "which": "^2.0.1" } }, + "date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "requires": { + "time-zone": "^1.0.0" + } + }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" } }, + "deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -3407,6 +5372,11 @@ "object-keys": "^1.0.12" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3416,6 +5386,12 @@ "esutils": "^2.0.2" } }, + "dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true + }, "electron-to-chromium": { "version": "1.3.857", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.857.tgz", @@ -3482,6 +5458,36 @@ "is-symbol": "^1.0.2" } }, + "esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -3761,6 +5767,27 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3773,6 +5800,25 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", + "dev": true, + "optional": true, + "peer": true + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3782,6 +5828,17 @@ "flat-cache": "^3.0.4" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -3802,17 +5859,39 @@ } }, "flatted": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz", - "integrity": "sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3832,6 +5911,12 @@ "dev": true, "peer": true }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -4025,6 +6110,14 @@ "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", "dev": true }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "optional": true, + "peer": true + }, "is-number-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", @@ -4067,6 +6160,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4114,6 +6213,12 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "jszip": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.8.0.tgz", @@ -4155,6 +6260,12 @@ "strip-bom": "^3.0.0" } }, + "local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "dev": true + }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -4165,6 +6276,12 @@ "path-exists": "^3.0.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -4183,6 +6300,15 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, + "loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4192,6 +6318,57 @@ "yallist": "^4.0.0" } }, + "magic-string": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.13" + } + }, + "md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "requires": { + "blueimp-md5": "^2.10.0" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "optional": true, + "peer": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4207,6 +6384,34 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "mlly": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.2.1.tgz", + "integrity": "sha512-1aMEByaWgBPEbWV2BOPEMySRrzl7rIHXmQxam4DM8jVjalTQDjpN2ZKOLUrwyhfZQO7IXHml2StcHMhooDeEEQ==", + "dev": true, + "requires": { + "acorn": "^8.8.2", + "pathe": "^1.1.0", + "pkg-types": "^1.0.3", + "ufo": "^1.1.2" + }, + "dependencies": { + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + } + } + }, + "mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "dev": true, + "optional": true, + "peer": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4220,6 +6425,12 @@ "dev": true, "peer": true }, + "nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4403,6 +6614,32 @@ "pify": "^3.0.0" } }, + "pathe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz", + "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "optional": true, + "peer": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -4418,6 +6655,17 @@ "find-up": "^2.1.0" } }, + "pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "requires": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, "pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", @@ -4427,12 +6675,42 @@ "find-up": "^2.1.0" } }, + "postcss": { + "version": "8.4.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", + "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", + "dev": true, + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -4444,12 +6722,31 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "optional": true, + "peer": true + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -4513,6 +6810,14 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "optional": true, + "peer": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4522,6 +6827,26 @@ "glob": "^7.1.3" } }, + "rollup": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.22.0.tgz", + "integrity": "sha512-imsigcWor5Y/dC0rz2q0bBt9PabcL3TORry2hAa6O6BuMvY71bqHyfReAz5qyAqiQATD1m70qdntqBfBQjVWpQ==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -4556,6 +6881,25 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "sirv": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", + "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^3.0.0" + } + }, "slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -4574,6 +6918,12 @@ "dev": true, "peer": true }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -4612,6 +6962,18 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "std-env": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", + "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==", + "dev": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -4672,6 +7034,23 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "strip-literal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.1.tgz", + "integrity": "sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==", + "dev": true, + "requires": { + "acorn": "^8.8.2" + }, + "dependencies": { + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + } + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4721,6 +7100,30 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true + }, + "tinybench": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", + "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", + "dev": true + }, + "tinypool": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.5.0.tgz", + "integrity": "sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==", + "dev": true + }, + "tinyspy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.0.tgz", + "integrity": "sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ==", + "dev": true + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -4728,6 +7131,25 @@ "dev": true, "peer": true }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "optional": true, + "peer": true + }, "tsconfig-paths": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", @@ -4748,12 +7170,24 @@ "prelude-ls": "^1.2.1" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "ufo": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.1.2.tgz", + "integrity": "sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==", + "dev": true + }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -4796,6 +7230,79 @@ "spdx-expression-parse": "^3.0.0" } }, + "vite": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.8.tgz", + "integrity": "sha512-uYB8PwN7hbMrf4j1xzGDk/lqjsZvCDbt/JC5dyfxc19Pg8kRm14LinK/uq+HSLNswZEoKmweGdtpbnxRtrAXiQ==", + "dev": true, + "requires": { + "esbuild": "^0.17.5", + "fsevents": "~2.3.2", + "postcss": "^8.4.23", + "rollup": "^3.21.0" + } + }, + "vite-node": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.31.1.tgz", + "integrity": "sha512-BajE/IsNQ6JyizPzu9zRgHrBwczkAs0erQf/JRpgTIESpKvNj9/Gd0vxX905klLkb0I0SJVCKbdrl5c6FnqYKA==", + "dev": true, + "requires": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.2.0", + "pathe": "^1.1.0", + "picocolors": "^1.0.0", + "vite": "^3.0.0 || ^4.0.0" + } + }, + "vitest": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.31.1.tgz", + "integrity": "sha512-/dOoOgzoFk/5pTvg1E65WVaobknWREN15+HF+0ucudo3dDG/vCZoXTQrjIfEaWvQXmqScwkRodrTbM/ScMpRcQ==", + "dev": true, + "requires": { + "@types/chai": "^4.3.5", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "@vitest/expect": "0.31.1", + "@vitest/runner": "0.31.1", + "@vitest/snapshot": "0.31.1", + "@vitest/spy": "0.31.1", + "@vitest/utils": "0.31.1", + "acorn": "^8.8.2", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.7", + "concordance": "^5.0.4", + "debug": "^4.3.4", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.0", + "pathe": "^1.1.0", + "picocolors": "^1.0.0", + "std-env": "^3.3.2", + "strip-literal": "^1.0.1", + "tinybench": "^2.5.0", + "tinypool": "^0.5.0", + "vite": "^3.0.0 || ^4.0.0", + "vite-node": "0.31.1", + "why-is-node-running": "^2.2.2" + }, + "dependencies": { + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + } + } + }, + "well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4818,6 +7325,16 @@ "is-symbol": "^1.0.3" } }, + "why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "requires": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -4841,6 +7358,12 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true } } } diff --git a/package.json b/package.json index 8abd01f..0c07269 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !", "main": "main.js", "scripts": { - "test": "node test" + "test": "vitest" }, "repository": { "type": "git", @@ -26,14 +26,17 @@ "author": "Mathieu Colmon", "license": "ISC", "dependencies": { + "axios": "^1.5.0", "jszip": "^3.7.1", "ws": "^7.4.3" }, "devDependencies": { "@babel/eslint-parser": "^7.15.7", "@mathieuc/console": "^1.0.1", + "dotenv": "^16.3.1", "eslint": "^7.25.0", "eslint-config-airbnb-base": "^14.2.1", - "eslint-plugin-import": "^2.22.1" + "eslint-plugin-import": "^2.22.1", + "vitest": "^0.31.1" } } diff --git a/src/FormData.js b/src/FormData.js deleted file mode 100644 index aab3af6..0000000 --- a/src/FormData.js +++ /dev/null @@ -1,36 +0,0 @@ -/** @class */ -class FormData { - #string = ''; - - #boundary = ''; - - get boundary() { - return this.#boundary; - } - - constructor() { - const random = (Math.random() * 10 ** 20).toString(36); - this.#boundary = `${random}`; - this.#string = `--${this.boundary}`; - } - - /** - * Adds a property to the FormData object - * @param {string} key Property key - * @param {string} value Property value - */ - append(key, value) { - this.#string += `\r\nContent-Disposition: form-data; name="${key}"`; - this.#string += `\r\n\r\n${value}`; - this.#string += `\r\n--${this.boundary}`; - } - - /** - * @returns {string} - */ - toString() { - return `${this.#string}--`; - } -} - -module.exports = FormData; diff --git a/src/classes/PinePermManager.js b/src/classes/PinePermManager.js index 02d03c8..f5f55b6 100644 --- a/src/classes/PinePermManager.js +++ b/src/classes/PinePermManager.js @@ -1,5 +1,4 @@ -const request = require('../request'); -const FormData = require('../FormData'); +const axios = require('axios'); /** * @typedef {Object} AuthorizationUser @@ -18,14 +17,17 @@ class PinePermManager { /** * Creates a PinePermManager instance - * @param {string} sessionId Token from sessionid cookie + * @param {string} sessionId Token from `sessionid` cookie + * @param {string} signature Signature cookie * @param {string} pineId Indicator ID (Like: PUB;XXXXXXXXXXXXXXXXXXXXX) */ - constructor(sessionId, pineId) { + constructor(sessionId, signature, pineId) { if (!sessionId) throw new Error('Please provide a SessionID'); + if (!signature) throw new Error('Please provide a Signature'); if (!pineId) throw new Error('Please provide a PineID'); - this.pineId = pineId; this.sessionId = sessionId; + this.signature = signature; + this.pineId = pineId; } /** @@ -40,20 +42,23 @@ class PinePermManager { * @returns {AuthorizationUser[]} */ async getUsers(limit = 10, order = '-created') { - const { data } = await request({ - method: 'POST', - host: 'www.tradingview.com', - path: `/pine_perm/list_users/?limit=${limit}&order_by=${order}`, - headers: { - origin: 'https://www.tradingview.com', - 'Content-Type': 'application/x-www-form-urlencoded', - cookie: `sessionid=${this.sessionId}`, - }, - }, false, `pine_id=${this.pineId.replace(/;/g, '%3B')}`); - - if (!data.results) throw new Error('Wrong sessionId or pineId'); - - return data.results; + try { + const { data } = await axios.post( + `https://www.tradingview.com/pine_perm/list_users/?limit=${limit}&order_by=${order}`, + `pine_id=${this.pineId.replace(/;/g, '%3B')}`, + { + headers: { + origin: 'https://www.tradingview.com', + 'Content-Type': 'application/x-www-form-urlencoded', + cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`, + }, + }, + ); + + return data.results; + } catch (e) { + throw new Error(e.response.data.detail || 'Wrong credentials or pineId'); + } } /** @@ -63,26 +68,31 @@ class PinePermManager { * @returns {'ok' | 'exists' | null} */ async addUser(username, expiration = null) { - const formData = new FormData(); - formData.append('pine_id', this.pineId); - formData.append('username_recip', username); - if (expiration && expiration instanceof Date) { - formData.append('expiration', expiration.toString()); + try { + const { data } = await axios.post( + 'https://www.tradingview.com/pine_perm/add/', + `pine_id=${ + this.pineId.replace(/;/g, '%3B') + }&username_recip=${ + username + }${ + expiration && expiration instanceof Date + ? `&expiration=${expiration.toISOString()}` + : '' + }`, + { + headers: { + origin: 'https://www.tradingview.com', + 'Content-Type': 'application/x-www-form-urlencoded', + cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`, + }, + }, + ); + + return data.status; + } catch (e) { + throw new Error(e.response.data.detail || 'Wrong credentials or pineId'); } - - const { data } = await request({ - method: 'POST', - host: 'www.tradingview.com', - path: '/pine_perm/add/', - headers: { - origin: 'https://www.tradingview.com', - 'Content-Type': `multipart/form-data; boundary=${formData.boundary}`, - cookie: `sessionid=${this.sessionId}`, - }, - }, false, formData.toString()); - - if (!data.status) throw new Error('Wrong sessionId or pineId'); - return data.status; } /** @@ -92,26 +102,31 @@ class PinePermManager { * @returns {'ok' | null} */ async modifyExpiration(username, expiration = null) { - const formData = new FormData(); - formData.append('pine_id', this.pineId); - formData.append('username_recip', username); - if (expiration && expiration instanceof Date) { - formData.append('expiration', expiration.toISOString()); + try { + const { data } = await axios.post( + 'https://www.tradingview.com/pine_perm/modify_user_expiration/', + `pine_id=${ + this.pineId.replace(/;/g, '%3B') + }&username_recip=${ + username + }${ + expiration && expiration instanceof Date + ? `&expiration=${expiration.toISOString()}` + : '' + }`, + { + headers: { + origin: 'https://www.tradingview.com', + 'Content-Type': 'application/x-www-form-urlencoded', + cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`, + }, + }, + ); + + return data.status; + } catch (e) { + throw new Error(e.response.data.detail || 'Wrong credentials or pineId'); } - - const { data } = await request({ - method: 'POST', - host: 'www.tradingview.com', - path: '/pine_perm/modify_user_expiration/', - headers: { - origin: 'https://www.tradingview.com', - 'Content-Type': `multipart/form-data; boundary=${formData.boundary}`, - cookie: `sessionid=${this.sessionId}`, - }, - }, false, formData.toString()); - - if (!data.status) throw new Error('Wrong sessionId or pineId'); - return data.status; } /** @@ -120,23 +135,23 @@ class PinePermManager { * @returns {'ok' | null} */ async removeUser(username) { - const formData = new FormData(); - formData.append('pine_id', this.pineId); - formData.append('username_recip', username); - - const { data } = await request({ - method: 'POST', - host: 'www.tradingview.com', - path: '/pine_perm/remove/', - headers: { - origin: 'https://www.tradingview.com', - 'Content-Type': `multipart/form-data; boundary=${formData.boundary}`, - cookie: `sessionid=${this.sessionId}`, - }, - }, false, formData.toString()); - - if (!data.status) throw new Error('Wrong sessionId or pineId'); - return data.status; + try { + const { data } = await axios.post( + 'https://www.tradingview.com/pine_perm/remove/', + `pine_id=${this.pineId.replace(/;/g, '%3B')}&username_recip=${username}`, + { + headers: { + origin: 'https://www.tradingview.com', + 'Content-Type': 'application/x-www-form-urlencoded', + cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`, + }, + }, + ); + + return data.status; + } catch (e) { + throw new Error(e.response.data.detail || 'Wrong credentials or pineId'); + } } } diff --git a/src/miscRequests.js b/src/miscRequests.js index 1af6d0b..b8ca27f 100644 --- a/src/miscRequests.js +++ b/src/miscRequests.js @@ -1,31 +1,19 @@ const os = require('os'); -const https = require('https'); -const request = require('./request'); -const FormData = require('./FormData'); +const axios = require('axios'); -const PinePermManager = require('./classes/PinePermManager'); const PineIndicator = require('./classes/PineIndicator'); +const validateStatus = (status) => status < 500; + const indicators = ['Recommend.Other', 'Recommend.All', 'Recommend.MA']; const builtInIndicList = []; async function fetchScanData(tickers = [], type = '', columns = []) { - let { data } = await request({ - method: 'POST', - hostname: 'scanner.tradingview.com', - path: `/${type}/scan`, - headers: { - 'Content-Type': 'application/json', - }, - }, true, JSON.stringify({ symbols: { tickers }, columns })); - - if (!data.startsWith('{')) throw new Error('Wrong screener or symbol'); - - try { - data = JSON.parse(data); - } catch (e) { - throw new Error('Can\'t parse server response'); - } + const { data } = await axios.post(`https://scanner.tradingview.com/${type}/scan`, { + validateStatus, + symbols: { tickers }, + columns, + }); return data; } @@ -53,40 +41,40 @@ async function fetchScanData(tickers = [], type = '', columns = []) { * }} Periods */ -/** - * @typedef {string | 'forex' | 'crypto' - * | 'america' | 'australia' | 'canada' | 'egypt' - * | 'germany' | 'india' | 'israel' | 'italy' - * | 'luxembourg' | 'poland' | 'sweden' | 'turkey' - * | 'uk' | 'vietnam'} Screener - * You can use `getScreener(exchange)` function for non-forex and non-crypto markets. - */ +// /** +// * @typedef {string | 'forex' | 'crypto' +// * | 'america' | 'australia' | 'canada' | 'egypt' +// * | 'germany' | 'india' | 'israel' | 'italy' +// * | 'luxembourg' | 'poland' | 'sweden' | 'turkey' +// * | 'uk' | 'vietnam'} Screener +// * You can use `getScreener(exchange)` function for non-forex and non-crypto markets. +// */ module.exports = { - /** - * Get a screener from an exchange - * @function getScreener - * @param {string} exchange Example: BINANCE, EURONEXT, NASDAQ - * @returns {Screener} - */ - getScreener(exchange) { - const e = exchange.toUpperCase(); - if (['NASDAQ', 'NYSE', 'NYSE ARCA', 'OTC'].includes(e)) return 'america'; - if (['ASX'].includes(e)) return 'australia'; - if (['TSX', 'TSXV', 'CSE', 'NEO'].includes(e)) return 'canada'; - if (['EGX'].includes(e)) return 'egypt'; - if (['FWB', 'SWB', 'XETR'].includes(e)) return 'germany'; - if (['BSE', 'NSE'].includes(e)) return 'india'; - if (['TASE'].includes(e)) return 'israel'; - if (['MIL', 'MILSEDEX'].includes(e)) return 'italy'; - if (['LUXSE'].includes(e)) return 'luxembourg'; - if (['NEWCONNECT'].includes(e)) return 'poland'; - if (['NGM'].includes(e)) return 'sweden'; - if (['BIST'].includes(e)) return 'turkey'; - if (['LSE', 'LSIN'].includes(e)) return 'uk'; - if (['HNX'].includes(e)) return 'vietnam'; - return exchange.toLowerCase(); - }, + // /** + // * Get a screener from an exchange + // * @function getScreener + // * @param {string} exchange Example: BINANCE, EURONEXT, NASDAQ + // * @returns {Screener} + // */ + // getScreener(exchange) { + // const e = exchange.toUpperCase(); + // if (['NASDAQ', 'NYSE', 'NYSE ARCA', 'OTC'].includes(e)) return 'america'; + // if (['ASX'].includes(e)) return 'australia'; + // if (['TSX', 'TSXV', 'CSE', 'NEO'].includes(e)) return 'canada'; + // if (['EGX'].includes(e)) return 'egypt'; + // if (['FWB', 'SWB', 'XETR'].includes(e)) return 'germany'; + // if (['BSE', 'NSE'].includes(e)) return 'india'; + // if (['TASE'].includes(e)) return 'israel'; + // if (['MIL', 'MILSEDEX'].includes(e)) return 'italy'; + // if (['LUXSE'].includes(e)) return 'luxembourg'; + // if (['NEWCONNECT'].includes(e)) return 'poland'; + // if (['NGM'].includes(e)) return 'sweden'; + // if (['BIST'].includes(e)) return 'turkey'; + // if (['LSE', 'LSIN'].includes(e)) return 'uk'; + // if (['HNX'].includes(e)) return 'vietnam'; + // return 'global'; + // }, /** * Get technical analysis @@ -138,20 +126,25 @@ module.exports = { * @returns {Promise} Search results */ async searchMarket(search, filter = '') { - const { data } = await request({ - host: 'symbol-search.tradingview.com', - path: `/symbol_search/?text=${search.replace(/ /g, '%20')}&type=${filter}`, - origin: 'https://www.tradingview.com', - }); + const { data } = await axios.get( + `https://symbol-search.tradingview.com/symbol_search/?text=${search.replace(/ /g, '%20')}&type=${filter}`, + { + validateStatus, + headers: { + origin: 'https://www.tradingview.com', + }, + }, + ); return data.map((s) => { const exchange = s.exchange.split(' ')[0]; const id = `${exchange}:${s.symbol}`; - const screener = (['forex', 'crypto'].includes(s.type) - ? s.type - : this.getScreener(exchange) - ); + // const screener = (['forex', 'crypto'].includes(s.type) + // ? s.type + // : this.getScreener(exchange) + // ); + const screener = 'global'; return { id, @@ -189,17 +182,18 @@ module.exports = { async searchIndicator(search = '') { if (!builtInIndicList.length) { await Promise.all(['standard', 'candlestick', 'fundamental'].map(async (type) => { - builtInIndicList.push(...(await request({ - host: 'pine-facade.tradingview.com', - path: `/pine-facade/list/?filter=${type}`, - })).data); + const { data } = await axios.get( + `https://pine-facade.tradingview.com/pine-facade/list/?filter=${type}`, + { validateStatus }, + ); + builtInIndicList.push(...data); })); } - const { data } = await request({ - host: 'www.tradingview.com', - path: `/pubscripts-suggest-json/?search=${search.replace(/ /g, '%20')}`, - }); + const { data } = await axios.get( + `https://www.tradingview.com/pubscripts-suggest-json/?search=${search.replace(/ /g, '%20')}`, + { validateStatus }, + ); function norm(str = '') { return str.toUpperCase().replace(/[^A-Z]/g, ''); @@ -255,16 +249,10 @@ module.exports = { async getIndicator(id, version = 'last') { const indicID = id.replace(/ |%/g, '%25'); - let { data } = await request({ - host: 'pine-facade.tradingview.com', - path: `/pine-facade/translate/${indicID}/${version}`, - }, true); - - try { - data = JSON.parse(data); - } catch (e) { - throw new Error(`Inexistent or unsupported indicator: '${id}'`); - } + const { data } = await axios.get( + `https://pine-facade.tradingview.com/pine-facade/translate/${indicID}/${version}`, + { validateStatus }, + ); if (!data.success || !data.result.metaInfo || !data.result.metaInfo.inputs) { throw new Error(`Inexistent or unsupported indicator: "${data.reason}"`); @@ -342,6 +330,7 @@ module.exports = { * @prop {number} notifications.following Notification from following accounts * @prop {string} session User session * @prop {string} sessionHash User session hash + * @prop {string} signature User session signature * @prop {string} privateChannel User private channel * @prop {string} authToken User auth token * @prop {Date} joinDate Account creation date @@ -357,21 +346,20 @@ module.exports = { * @returns {Promise} Token */ async loginUser(username, password, remember = true, UA = 'TWAPI/3.0') { - const formData = new FormData(); - formData.append('username', username); - formData.append('password', password); - if (remember) formData.append('remember', 'on'); - - const { data, cookies } = await request({ - method: 'POST', - host: 'www.tradingview.com', - path: '/accounts/signin/', - headers: { - referer: 'https://www.tradingview.com', - 'Content-Type': `multipart/form-data; boundary=${formData.boundary}`, - 'User-agent': `${UA} (${os.version()}; ${os.platform()}; ${os.arch()})`, + const { data, headers } = await axios.post( + 'https://www.tradingview.com/accounts/signin/', + `username=${username}&password=${password}${remember ? '&remember=on' : ''}`, + { + validateStatus, + headers: { + referer: 'https://www.tradingview.com', + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-agent': `${UA} (${os.version()}; ${os.platform()}; ${os.arch()})`, + }, }, - }, false, formData.toString()); + ); + + const cookies = headers['set-cookie']; if (data.error) throw new Error(data.error); @@ -408,43 +396,36 @@ module.exports = { * @returns {Promise} Token */ async getUser(session, signature = '', location = 'https://www.tradingview.com/') { - return new Promise((cb, err) => { - https.get(location, { - headers: { cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` }, - }, (res) => { - let rs = ''; - res.on('data', (d) => { rs += d; }); - res.on('end', async () => { - if (res.headers.location && location !== res.headers.location) { - cb(await module.exports.getUser(session, signature, res.headers.location)); - return; - } - if (rs.includes('auth_token')) { - cb({ - id: /"id":([0-9]{1,10}),/.exec(rs)[1], - username: /"username":"(.*?)"/.exec(rs)[1], - firstName: /"first_name":"(.*?)"/.exec(rs)[1], - lastName: /"last_name":"(.*?)"/.exec(rs)[1], - reputation: parseFloat(/"reputation":(.*?),/.exec(rs)[1] || 0), - following: parseFloat(/,"following":([0-9]*?),/.exec(rs)[1] || 0), - followers: parseFloat(/,"followers":([0-9]*?),/.exec(rs)[1] || 0), - notifications: { - following: parseFloat(/"notification_count":\{"following":([0-9]*),/.exec(rs)[1] || 0), - user: parseFloat(/"notification_count":\{"following":[0-9]*,"user":([0-9]*)/.exec(rs)[1] || 0), - }, - session, - signature, - sessionHash: /"session_hash":"(.*?)"/.exec(rs)[1], - privateChannel: /"private_channel":"(.*?)"/.exec(rs)[1], - authToken: /"auth_token":"(.*?)"/.exec(rs)[1], - joinDate: new Date(/"date_joined":"(.*?)"/.exec(rs)[1] || 0), - }); - } else err(new Error('Wrong or expired sessionid/signature')); - }); - - res.on('error', err); - }).end(); + const { data } = await axios.get(location, { + validateStatus, + headers: { + cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`, + }, }); + + if (data.includes('auth_token')) { + return { + id: /"id":([0-9]{1,10}),/.exec(data)?.[1], + username: /"username":"(.*?)"/.exec(data)?.[1], + firstName: /"first_name":"(.*?)"/.exec(data)?.[1], + lastName: /"last_name":"(.*?)"/.exec(data)?.[1], + reputation: parseFloat(/"reputation":(.*?),/.exec(data)?.[1] || 0), + following: parseFloat(/,"following":([0-9]*?),/.exec(data)?.[1] || 0), + followers: parseFloat(/,"followers":([0-9]*?),/.exec(data)?.[1] || 0), + notifications: { + following: parseFloat(/"notification_count":\{"following":([0-9]*),/.exec(data)?.[1] || 0), + user: parseFloat(/"notification_count":\{"following":[0-9]*,"user":([0-9]*)/.exec(data)?.[1] || 0), + }, + session, + signature, + sessionHash: /"session_hash":"(.*?)"/.exec(data)?.[1], + privateChannel: /"private_channel":"(.*?)"/.exec(data)?.[1], + authToken: /"auth_token":"(.*?)"/.exec(data)?.[1], + joinDate: new Date(/"date_joined":"(.*?)"/.exec(data)?.[1] || 0), + }; + } + + throw new Error('Wrong or expired sessionid/signature'); }, /** @@ -455,44 +436,29 @@ module.exports = { * @returns {Promise} Search results */ async getPrivateIndicators(session, signature = '') { - return new Promise((cb, err) => { - https.get('https://pine-facade.tradingview.com/pine-facade/list?filter=saved', { - headers: { cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` }, - }, (res) => { - let rs = ''; - res.on('data', (d) => { rs += d; }); - res.on('end', async () => { - try { - rs = JSON.parse(rs); - } catch (error) { - err(new Error('Can\'t parse private indicator list')); - return; - } - - cb(rs.map((ind) => ({ - id: ind.scriptIdPart, - version: ind.version, - name: ind.scriptName, - author: { - id: -1, - username: '@ME@', - }, - image: ind.imageUrl, - access: 'private', - source: ind.scriptSource, - type: (ind.extra && ind.extra.kind) ? ind.extra.kind : 'study', - get() { - return module.exports.getIndicator(ind.scriptIdPart, ind.version); - }, - getManager() { - return new PinePermManager(ind.scriptIdPart); - }, - }))); - }); - - res.on('error', err); - }).end(); + const { data } = await axios.get('https://pine-facade.tradingview.com/pine-facade/list?filter=saved', { + validateStatus, + headers: { + cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`, + }, }); + + return data.map((ind) => ({ + id: ind.scriptIdPart, + version: ind.version, + name: ind.scriptName, + author: { + id: -1, + username: '@ME@', + }, + image: ind.imageUrl, + access: 'private', + source: ind.scriptSource, + type: (ind.extra && ind.extra.kind) ? ind.extra.kind : 'study', + get() { + return module.exports.getIndicator(ind.scriptIdPart, ind.version); + }, + })); }, /** @@ -500,6 +466,7 @@ module.exports = { * @typedef {Object} UserCredentials * @prop {number} id User ID * @prop {string} session User session ('sessionid' cookie) + * @prop {string} [signature] User session signature ('sessionid_sign' cookie) */ /** @@ -510,16 +477,23 @@ module.exports = { * @returns {Promise} Token */ async getChartToken(layout, credentials = {}) { - const creds = credentials.id && credentials.session; - const userID = creds ? credentials.id : -1; - const session = creds ? credentials.session : null; - const signature = creds ? credentials.signature : null; - - const { data } = await request({ - host: 'www.tradingview.com', - path: `/chart-token/?image_url=${layout}&user_id=${userID}`, - headers: { cookie: session ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` : '' }, - }); + const { id, session, signature } = ( + credentials.id && credentials.session + ? credentials + : { id: -1, session: null, signature: null } + ); + + const { data } = await axios.get( + `https://www.tradingview.com/chart-token/?image_url=${layout}&user_id=${id}`, + { + validateStatus, + headers: { + cookie: session + ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` + : '', + }, + }, + ); if (!data.token) throw new Error('Wrong layout or credentials'); @@ -557,18 +531,21 @@ module.exports = { * @param {number} [chartID] Chart ID * @returns {Promise} Drawings */ - async getDrawings(layout, symbol = '', credentials = {}, chartID = 1) { + async getDrawings(layout, symbol = '', credentials = {}, chartID = '_shared') { const chartToken = await module.exports.getChartToken(layout, credentials); - const creds = credentials.id && credentials.session; - const session = creds ? credentials.session : null; - const signature = creds ? credentials.signature : null; - - const { data } = await request({ - host: 'charts-storage.tradingview.com', - path: `/charts-storage/layout/${layout}/sources?chart_id=${chartID - }&jwt=${chartToken}${symbol ? `&symbol=${symbol}` : ''}`, - headers: { cookie: session ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` : '' }, - }); + + const { data } = await axios.get( + `https://charts-storage.tradingview.com/charts-storage/get/layout/${ + layout + }/sources?chart_id=${ + chartID + }&jwt=${ + chartToken + }${ + (symbol ? `&symbol=${symbol}` : '') + }`, + { validateStatus }, + ); if (!data.payload) throw new Error('Wrong layout, user credentials, or chart id.'); diff --git a/src/request.js b/src/request.js deleted file mode 100644 index fa6e057..0000000 --- a/src/request.js +++ /dev/null @@ -1,37 +0,0 @@ -const https = require('https'); - -/** - * @param {https.RequestOptions} options HTTPS Request options - * @param {boolean} [raw] Get raw or JSON data - * @param {string} [content] Request body content - * @returns {Promise<{ data: (string | object | array), cookies: string[] }>} Result - */ -function request(options = {}, raw = false, content = '') { - return new Promise((cb, err) => { - const req = https.request(options, (res) => { - let data = ''; - res.on('data', (c) => { data += c; }); - res.on('end', () => { - if (raw) { - cb({ data, cookies: res.headers['set-cookie'] }); - return; - } - - try { - data = JSON.parse(data); - } catch (error) { - console.log(data); - err(new Error('Can\'t parse server response')); - return; - } - - cb({ data, cookies: res.headers['set-cookie'] }); - }); - }); - - req.on('error', err); - req.end(content); - }); -} - -module.exports = request; diff --git a/test.js b/test.js deleted file mode 100644 index 72db92d..0000000 --- a/test.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable no-continue */ -/* eslint-disable no-await-in-loop */ -/* eslint-disable import/no-dynamic-require */ -/* eslint-disable no-restricted-syntax */ -/* eslint-disable global-require */ - -const fs = require('fs'); -require('@mathieuc/console')( - '§', // Character you want to use (defaut: '§') - true, // Active timestamp (defaut: false) -); - -const TESTS = {}; - -function err(name) { - return (...msg) => { - TESTS[name].errors += 1; - console.error('§30§101 > §0', ...msg); - }; -} - -function warn(name) { - return (...msg) => { - TESTS[name].warnings += 1; - console.warn('§30§103 > §0', ...msg); - }; -} - -function log() { - return (...msg) => console.log('§30§107 > §0', ...msg); -} - -function success() { - return (...msg) => console.info('§30§102 > §0', ...msg); -} - -(async () => { - console.info('§90§30§104 ==== Starting tests ==== §0'); - - for (const file of fs.readdirSync('./tests').filter((f) => f.endsWith('.js'))) { - if (process.argv[2] && !file.startsWith(process.argv[2])) continue; - const test = require(`./tests/${file}`); - const name = file.replace(/\.js/g, ''); - TESTS[name] = { errors: 0, warnings: 0 }; - console.log(`§90§30§107 ${name}`); - await new Promise((cb) => test(log(), success(), warn(name), err(name), cb)); - } - - console.info('§90§30§104 ==== ALL TESTS DONE ==== §0\n'); - - let errored = false; - - Object.keys(TESTS).forEach((t) => { - let color = '2'; - if (TESTS[t].warnings) color = '3'; - if (TESTS[t].errors) { - color = '1'; - errored = true; - } - - console.info(`§90§30§10${color} ${t} §0 §91E§0: ${TESTS[t].errors}§0 §93W§0: ${TESTS[t].warnings}`); - }); - - process.exit(errored ? 1 : 0); -})(); - -setTimeout(() => { - console.log('§30§101 TIMEOUT §0'); - throw new Error('Timeout'); -}, 60000); diff --git a/tests/01. Search.js b/tests/01. Search.js deleted file mode 100644 index 4e3aca9..0000000 --- a/tests/01. Search.js +++ /dev/null @@ -1,17 +0,0 @@ -const TradingView = require('../main'); - -module.exports = async (log, success, warn, err, cb) => { - log('Searching market: \'BINANCE:\''); - const markets = await TradingView.searchMarket('BINANCE:'); - success('Found', markets.length, 'markets'); - - log('Searching indicator: \'RSI\''); - const indicators = await TradingView.searchIndicator('RSI'); - success('Found', indicators.length, 'indicators'); - - if (markets.length < 10 || indicators.length < 10) { - err('Not enough results'); - } - - cb(); -}; diff --git a/tests/02. QuoteSession.js b/tests/02. QuoteSession.js deleted file mode 100644 index 8acee93..0000000 --- a/tests/02. QuoteSession.js +++ /dev/null @@ -1,51 +0,0 @@ -const TradingView = require('../main'); - -module.exports = (log, success, warn, err, cb) => { - const client = new TradingView.Client(); - - client.onError((...error) => { - err('Client error', ...error); - }); - - const quoteSession = new client.Session.Quote({ - fields: 'all', - }); - - const BTC = new quoteSession.Market('BTCEUR'); - - BTC.onLoaded(() => { - success('BTCEUR LOADED'); - }); - - const keys = [ - 'volume', 'update_mode', 'type', 'timezone', - 'short_name', 'rtc_time', 'rtc', 'rchp', 'ch', - 'rch', 'provider_id', 'pro_name', 'pricescale', - 'prev_close_price', 'original_name', 'lp', - 'open_price', 'minmove2', 'minmov', 'lp_time', - 'low_price', 'is_tradable', 'high_price', - 'fractional', 'exchange', 'description', - 'current_session', 'currency_code', 'chp', - 'currency-logoid', 'base-currency-logoid', - ]; - - BTC.onData(async (data) => { - const rsKeys = Object.keys(data); - success('BTCEUR DATA'); - if (rsKeys.length <= 2) return; - - keys.forEach((k) => { - if (!rsKeys.includes(k)) { - err(`Missing '${k}' key in`, rsKeys); - } - }); - - quoteSession.delete(); - await client.end(); - cb(); - }); - - BTC.onError((...error) => { - err('BTCEUR ERROR:', error); - }); -}; diff --git a/tests/03. SimpleChart.js b/tests/03. SimpleChart.js deleted file mode 100644 index c2c6fd0..0000000 --- a/tests/03. SimpleChart.js +++ /dev/null @@ -1,59 +0,0 @@ -const TradingView = require('../main'); - -module.exports = async (log, success, warn, err, cb) => { - const client = new TradingView.Client(); - - client.onError((...error) => { - err('Client error', error); - }); - - const chart = new client.Session.Chart(); - - chart.setMarket('BINANCE:BTCEUR', { // Set the market - timeframe: 'D', - }); - - chart.onError((...error) => { - err('Chart error', error); - }); - - chart.onSymbolLoaded(() => { // When the symbol is successfully loaded - success(`Market "${chart.infos.description}" loaded !`); - }); - - // Wait 3 seconds and set the market to BINANCE:ETHEUR - setTimeout(() => { - log('Setting market to BINANCE:ETHEUR...'); - chart.setMarket('BINANCE:ETHEUR', { - timeframe: 'D', - }); - }, 2000); - - // Wait 4 seconds and set the timeframe to 15 minutes - setTimeout(() => { - log('Setting timeframe to 15 minutes...'); - chart.setSeries('15'); - }, 3000); - - // Wait 5 seconds and set the chart type to "Heikin Ashi" - setTimeout(() => { - log('Setting the chart type to "Heikin Ashi"...'); - chart.setMarket('BINANCE:ETHEUR', { - timeframe: 'D', - type: 'HeikinAshi', - }); - }, 4000); - - // Wait 7 seconds and close the chart - setTimeout(() => { - log('Closing the chart...'); - chart.delete(); - }, 5000); - - // Wait 8 seconds and close the client - setTimeout(async () => { - log('Closing the client...'); - await client.end(); - cb(); - }, 6000); -}; diff --git a/tests/04. Indicators.js b/tests/04. Indicators.js deleted file mode 100644 index 82bbfc7..0000000 --- a/tests/04. Indicators.js +++ /dev/null @@ -1,96 +0,0 @@ -const TradingView = require('../main'); - -module.exports = async (log, success, warn, err, cb) => { - const client = new TradingView.Client(); - - client.onError((...error) => { - err('Client error', error); - }); - - const chart = new client.Session.Chart(); - chart.setMarket('BINANCE:BTCEUR', { - timeframe: '60', - }); - - chart.onError((...error) => { - err('Chart error', error); - }); - - TradingView.getIndicator('STD;Supertrend%Strategy').then((indicator) => { - indicator.setOption('commission_type', 'percent'); - indicator.setOption('commission_value', 0); - indicator.setOption('initial_capital', 25000); - indicator.setOption('default_qty_value', 20); - indicator.setOption('default_qty_type', 'percent_of_equity'); - indicator.setOption('currency', 'EUR'); - indicator.setOption('pyramiding', 10); - - const SuperTrend = new chart.Study(indicator); - - SuperTrend.onError((...error) => { - err('SuperTrend error', error[0]); - }); - - let QTY = 10; - - SuperTrend.onUpdate(async () => { - // MarketCipher B is a strategy so it sends a strategy report - const perfReport = SuperTrend.strategyReport.performance; - - success('Performances:', { - total: { - trades: perfReport.all.totalTrades, - perf: `${Math.round(perfReport.all.netProfitPercent * 10000) / 100} %`, - }, - buy: { - trades: perfReport.long.totalTrades, - perf: `${Math.round(perfReport.long.netProfitPercent * 10000) / 100} %`, - }, - sell: { - trades: perfReport.short.totalTrades, - perf: `${Math.round(perfReport.short.netProfitPercent * 10000) / 100} %`, - }, - }); - - if (QTY >= 50) { - SuperTrend.remove(); - chart.delete(); - await client.end(); - cb(); - return; - } - - QTY += 10; - log('TRY WITH', QTY, '%'); - setTimeout(() => { - indicator.setOption('default_qty_value', QTY); - SuperTrend.setIndicator(indicator); - }, 1000); - }); - }); - - TradingView.getIndicator('PUB;uA35GeckoTA2EfgI63SD2WCSmca4njxp').then((indicator) => { - indicator.setOption('Show_WT_Hidden_Divergences', true); - indicator.setOption('Show_Stoch_Regular_Divergences', true); - indicator.setOption('Show_Stoch_Hidden_Divergences', true); - - const CipherB = new chart.Study(indicator); - - CipherB.onError((...error) => { - err('MarketCipher B error:', error[0]); - }); - - CipherB.onUpdate(() => { - const last = CipherB.periods[0]; - // MarketCipher B is not a strategy so it only sends plots values - success('MarketCipher B last values:', { - VWAP: Math.round(last.VWAP * 1000) / 1000, - moneyFlow: (last.RSIMFIArea >= 0) ? 'POSITIVE' : 'NEGATIVE', - buyCircle: last.Buy_and_sell_circle && last.VWAP > 0, - sellCircle: last.Buy_and_sell_circle && last.VWAP < 0, - }); - - CipherB.remove(); - }); - }); -}; diff --git a/tests/05. BuiltInIndicator.js b/tests/05. BuiltInIndicator.js deleted file mode 100644 index d35fa2d..0000000 --- a/tests/05. BuiltInIndicator.js +++ /dev/null @@ -1,39 +0,0 @@ -const TradingView = require('../main'); - -module.exports = async (log, success, warn, err, cb) => { - const client = new TradingView.Client(); - - client.onError((...error) => { - err('Client error', error); - }); - - const chart = new client.Session.Chart(); - chart.setMarket('BINANCE:BTCEUR', { - timeframe: '60', - }); - - chart.onError((...error) => { - err('Chart error', error); - }); - - const volumeProfile = new TradingView.BuiltInIndicator('VbPFixed@tv-basicstudies-139!'); - - volumeProfile.setOption('first_bar_time', Date.now() - 10 ** 8); - - const VOL = new chart.Study(volumeProfile); - VOL.onUpdate(async () => { - VOL.graphic.horizHists - .filter((h) => h.lastBarTime === 0) // We only keep recent volume infos - .sort((a, b) => b.priceHigh - a.priceHigh) - .forEach((h) => { - success( - `~ ${Math.round((h.priceHigh + h.priceLow) / 2)} € :`, - `${'_'.repeat(h.rate[0] / 3)}${'_'.repeat(h.rate[1] / 3)}`, - ); - }); - - chart.delete(); - await client.end(); - cb(); - }); -}; diff --git a/tests/06. FakeReplayMode.js b/tests/06. FakeReplayMode.js deleted file mode 100644 index ae5e177..0000000 --- a/tests/06. FakeReplayMode.js +++ /dev/null @@ -1,57 +0,0 @@ -const TradingView = require('../main'); - -module.exports = async (log, success, warn, err, cb) => { - const client = new TradingView.Client(); - - client.onError((...error) => { - err('Client error', error); - }); - - const chart = new client.Session.Chart(); - chart.setMarket('BINANCE:BTCEUR', { - timeframe: 'D', - range: -1, - to: Math.round(Date.now() / 1000) - 86400 * 7, - }); - - chart.onError((...error) => { - err('Chart error', error); - }); - - let interval = NaN; - let consec = 1; - let lastTime = 0; - - chart.onUpdate(async () => { - const times = chart.periods.map((p) => p.time); - - if (lastTime === times[0]) { - consec += 1; - warn(`Same lastTime ${lastTime}, ${consec} times in a row`); - } else consec = 1; - [lastTime] = times; - - if (consec >= 10) { - chart.delete(); - await client.end(); - cb(); - } - - const intrval = times[0] - times[1]; - if (Number.isNaN(interval) && times.length >= 2) interval = intrval; - - if (!Number.isNaN(interval) && interval !== intrval) { - err(`Wrong interval: ${intrval} (should be ${interval})`); - } - - log('Next ->', times[0]); - - if ((times[0] + 86400) * 1000 > Date.now()) { - chart.delete(); - await client.end(); - cb(); - } - - chart.fetchMore(-1); - }); -}; diff --git a/tests/07. ReplayMode.js b/tests/07. ReplayMode.js deleted file mode 100644 index 7043f5c..0000000 --- a/tests/07. ReplayMode.js +++ /dev/null @@ -1,49 +0,0 @@ -const TradingView = require('../main'); - -module.exports = async (log, success, warn, err, cb) => { - const client = new TradingView.Client(); - - client.onError((...error) => { - err('Client error', error); - }); - - const chart = new client.Session.Chart(); - chart.setMarket('BINANCE:BTCEUR', { - timeframe: 'D', - replay: Math.round(Date.now() / 1000) - 86400 * 10, - range: 1, - }); - - chart.onError((...error) => { - err('Chart error', error); - }); - - let finished = false; - async function step() { - log('Next'); - await chart.replayStep(1); - if (!finished) step(); - } - - chart.onReplayLoaded(async () => { - log('START REPLAY MODE'); - await chart.replayStart(200); - - setTimeout(async () => { - await chart.replayStop(); - log('STOP REPLAY MODE'); - step(); - }, 1000); - }); - - chart.onReplayPoint((p) => { - success('Point ->', p); - }); - - chart.onReplayEnd(async () => { - finished = true; - chart.delete(); - await client.end(); - cb(); - }); -}; diff --git a/tests/08. CustomChartTypes.js b/tests/08. CustomChartTypes.js deleted file mode 100644 index 5de689a..0000000 --- a/tests/08. CustomChartTypes.js +++ /dev/null @@ -1,113 +0,0 @@ -const TradingView = require('../main'); - -module.exports = async (log, success, warn, err, cb) => { - const client = new TradingView.Client(); - - client.onError((...error) => { - err('Client error', error); - }); - - const chart = new client.Session.Chart(); - - chart.onError((...error) => { - err('Chart error', error); - }); - - /* (0s) Heikin Ashi chart */ - setTimeout(() => { - log('Setting chart type to: HeikinAshi'); - - chart.setMarket('BINANCE:BTCEUR', { - type: 'HeikinAshi', - timeframe: 'D', - }); - }, 0); - - /* (1s) Renko chart */ - setTimeout(() => { - log('Setting chart type to: Renko'); - - chart.setMarket('BINANCE:BTCEUR', { - type: 'Renko', - timeframe: 'D', - inputs: { - source: 'close', - sources: 'Close', - boxSize: 3, - style: 'ATR', - atrLength: 14, - wicks: true, - }, - }); - }, 1000); - - /* (2s) Line Break chart */ - setTimeout(() => { - log('Setting chart type to: LineBreak'); - - chart.setMarket('BINANCE:BTCEUR', { - type: 'LineBreak', - timeframe: 'D', - inputs: { - source: 'close', - lb: 3, - }, - }); - }, 2000); - - /* (3s) Kagi chart */ - setTimeout(() => { - log('Setting chart type to: Kagi'); - - chart.setMarket('BINANCE:BTCEUR', { - type: 'Kagi', - timeframe: 'D', - inputs: { - source: 'close', - style: 'ATR', - atrLength: 14, - reversalAmount: 1, - }, - }); - }, 3000); - - /* (4s) Point & Figure chart */ - setTimeout(() => { - log('Setting chart type to: PointAndFigure'); - - chart.setMarket('BINANCE:BTCEUR', { - type: 'PointAndFigure', - timeframe: 'D', - inputs: { - sources: 'Close', - reversalAmount: 3, - boxSize: 1, - style: 'ATR', - atrLength: 14, - oneStepBackBuilding: false, - }, - }); - }, 4000); - - /* (5s) Range chart */ - setTimeout(() => { - log('Setting chart type to: Range'); - - chart.setMarket('BINANCE:BTCEUR', { - type: 'Range', - timeframe: 'D', - inputs: { - range: 1, - phantomBars: false, - }, - }); - }, 5000); - - /* (6s) Delete chart, close client */ - setTimeout(async () => { - success('Closing client...'); - chart.delete(); - await client.end(); - cb(); - }, 6000); -}; diff --git a/tests/09. AllErrors.js b/tests/09. AllErrors.js deleted file mode 100644 index 6611d79..0000000 --- a/tests/09. AllErrors.js +++ /dev/null @@ -1,184 +0,0 @@ -const TradingView = require('../main'); - -module.exports = async (log, success, warn, err, cb) => { - const client = new TradingView.Client(); // Creates a websocket client - - const tests = [ - (next) => { /* Testing "Credentials error" */ - log('Testing "Credentials error" error:'); - - const client2 = new TradingView.Client({ - token: 'FAKE_CREDENTIALS', // Set wrong credentials - }); - - client2.onError((...error) => { - success('=> Client error:', error); - client2.end(); - next(); - }); - }, - - (next) => { /* Testing "Invalid symbol" */ - log('Testing "Invalid symbol" error:'); - - const chart = new client.Session.Chart(); - chart.onError((...error) => { // Listen for errors - success('=> Chart error:', error); - chart.delete(); - next(); - }); - - chart.setMarket('XXXXX'); // Set a wrong market - }, - - (next) => { /* Testing "Invalid timezone" */ - log('Testing "Invalid timezone" error:'); - - const chart = new client.Session.Chart(); - chart.onError((...error) => { // Listen for errors - success('=> Chart error:', error); - next(); - }); - - chart.setMarket('BINANCE:BTCEUR'); // Set a market - chart.setTimezone('Nowhere/Nowhere'); // Set a fake timezone - }, - - (next) => { /* Testing "Custom timeframe" */ - log('Testing "Custom timeframe" error:'); - - const chart = new client.Session.Chart(); - chart.onError((...error) => { // Listen for errors - success('=> Chart error:', error); - chart.delete(); - next(); - }); - - chart.setMarket('BINANCE:BTCEUR', { // Set a market - timeframe: '20', // Set a custom timeframe - /* - Timeframe '20' isn't available because we are - not logged in as a premium TradingView account - */ - }); - }, - - (next) => { /* Testing "Invalid timeframe" */ - log('Testing "Invalid timeframe" error:'); - - const chart = new client.Session.Chart(); - chart.onError((...error) => { // Listen for errors - success('=> Chart error:', error); - next(); - }); - - chart.setMarket('BINANCE:BTCEUR', { // Set a market - timeframe: 'XX', // Set a wrong timeframe - }); - }, - - (next) => { /* Testing "Study not auth" */ - log('Testing "Study not auth" error:'); - - const chart = new client.Session.Chart(); - chart.onError((...error) => { // Listen for errors - success('=> Chart error:', error); - next(); - }); - - chart.setMarket('BINANCE:BTCEUR', { // Set a market - timeframe: '15', - type: 'Renko', - }); - - chart.onUpdate(() => { - log('DATA', chart.periods[0]); - }); - }, - - (next) => { /* Testing "Set the market before" */ - log('Testing "Set the market before..." error:'); - - const chart = new client.Session.Chart(); - chart.onError((...error) => { // Listen for errors - success('=> Chart error:', error); - chart.delete(); - next(); - }); - - chart.setSeries('15'); // Set series before setting the market - }, - - (next) => { /* Testing "Inexistent indicator" */ - log('Testing "Inexistent indicator" error:'); - - TradingView.getIndicator('STD;XXXXXXX') - .catch((error) => { - success('=> API error:', [error.message]); - next(); - }); - }, - - async (next) => { /* Testing "Invalid value" */ - log('Testing "Invalid value" error:'); - - const chart = new client.Session.Chart(); - chart.setMarket('BINANCE:BTCEUR'); // Set a market - - const ST = await TradingView.getIndicator('STD;Supertrend'); - ST.setOption('Factor', -1); // This will cause an error - - const Supertrend = new chart.Study(ST); - Supertrend.onError((...error) => { - success('=> Study error:', error); - chart.delete(); - next(); - }); - }, - - async (next) => { /* Testing "Wrong or expired sessionid/signature" using getUser method */ - log('Testing "Wrong or expired sessionid/signature" error using getUser method:'); - - if (!process.env.SESSION) { - warn('=> Skipping test because no sessionid/signature was provided'); - next(); - return; - } - - try { - await TradingView.getUser(process.env.SESSION); - err('=> User found !'); - } catch (error) { - success('=> User not found:', error); - next(); - } - }, - - async (next) => { /* Testing "Wrong or expired sessionid/signature" using client */ - log('Testing "Wrong or expired sessionid/signature" error using client:'); - - if (!process.env.SESSION) { - warn('=> Skipping test because no sessionid/signature was provided'); - next(); - return; - } - - log('Creating a new client...'); - const client2 = new TradingView.Client({ - token: process.env.SESSION, - }); - - client2.onError((...error) => { - success('=> Client error:', error); - client2.end(); - next(); - }); - }, - ]; - - (async () => { - for (const t of tests) await new Promise(t); - success(`Crashtests ${tests.length}/${tests.length} done !`); - cb(); - })(); -}; diff --git a/tests/10. Authenticated.js b/tests/10. Authenticated.js deleted file mode 100644 index 2ccecb9..0000000 --- a/tests/10. Authenticated.js +++ /dev/null @@ -1,101 +0,0 @@ -const TradingView = require('../main'); - -const wait = (ms) => new Promise((cb) => { setTimeout(cb, ms); }); - -module.exports = async (log, success, warn, err, cb) => { - if (!process.env.SESSION || !process.env.SIGNATURE) { - warn('No sessionid/signature was provided'); - cb(); - return; - } - - log('Getting user info'); - - const userInfos = await TradingView.getUser(process.env.SESSION, process.env.SIGNATURE); - if (userInfos && userInfos.id) { - success('User info:', { - id: userInfos.id, - username: userInfos.username, - firstName: userInfos.firstName, - lastName: userInfos.lastName, - following: userInfos.following, - followers: userInfos.followers, - notifications: userInfos.notifications, - joinDate: userInfos.joinDate, - }); - } else err('Get user error. Result:', userInfos); - - await wait(1000); - - log('Getting user indicators'); - const userIndicators = await TradingView.getPrivateIndicators(process.env.SESSION); - - if (userIndicators) { - if (userIndicators.length === 0) warn('No private indicator found'); - else success('User indicators:', userIndicators.map((i) => i.name)); - } else err('Get indicators error. Result:', userIndicators); - - await wait(1000); - - log('Creating logged client'); - const client = new TradingView.Client({ - token: process.env.SESSION, - signature: process.env.SIGNATURE, - }); - - client.onError((...error) => { - err('Client error', error); - }); - - const chart = new client.Session.Chart(); - chart.onError((...error) => { - err('Chart error', error); - }); - - log('Setting market to BINANCE:BTCEUR...'); - chart.setMarket('BINANCE:BTCEUR', { timeframe: 'D' }); - - const checked = new Set(); - async function check(item) { - checked.add(item); - log('Checked:', [...checked], `(${checked.size}/${userIndicators.length + 1})`); - if (checked.size < userIndicators.length + 1) return; - - log('Closing client...'); - chart.delete(); - await client.end(); - success('Client closed'); - cb(); - } - - chart.onUpdate(async () => { - success('Market data:', { - name: chart.infos.pro_name, - description: chart.infos.short_description, - exchange: chart.infos.exchange, - price: chart.periods[0].close, - }); - - await check(Symbol.for('PRICE')); - }); - - await wait(1000); - - log('Loading indicators...'); - for (const indic of userIndicators) { - const privateIndic = await indic.get(); - log(`[${indic.name}] Loading indicator...`); - - const indicator = new chart.Study(privateIndic); - - indicator.onReady(() => { - success(`[${indic.name}] Indicator loaded !`); - }); - - indicator.onUpdate(async () => { - success(`[${indic.name}] Last plot:`, indicator.periods[0]); - await check(indic.id); - }); - } - log('Indicators loaded !'); -}; diff --git a/tests/allErrors.test.ts b/tests/allErrors.test.ts new file mode 100644 index 0000000..49f16c2 --- /dev/null +++ b/tests/allErrors.test.ts @@ -0,0 +1,235 @@ +import { describe, it, expect } from 'vitest'; +import TradingView from '../main'; + +const token = process.env.SESSION as string; +const signature = process.env.SIGNATURE as string; + +describe('AllErrors', () => { + const waitForError = (instance: any) => new Promise((resolve) => { + instance.onError((...error: string[]) => { + resolve(error); + }); + }); + + it('throws an error when an invalid token is set', async () => { + console.log('Testing "Credentials error" error:'); + + const client = new TradingView.Client({ + token: 'FAKE_CREDENTIALS', // Set wrong credentials + }); + + const error = await waitForError(client); + console.log('=> Client error:', error); + + expect(error).toBeDefined(); + expect(error[0]).toBe('Credentials error:'); + expect(error[1]).toBe('Wrong or expired sessionid/signature'); + expect(error.length).toBe(2); + }); + + it('throws an error when an invalid symbol is set', async () => { + console.log('Testing "invalid symbol" error:'); + + const client = new TradingView.Client(); + const chart = new client.Session.Chart(); + chart.setMarket('XXXXX'); + + const error = await waitForError(chart); + console.log('=> Chart error:', error); + + expect(error).toBeDefined(); + expect(error[0]).toBe('(ser_1) Symbol error:'); + expect(error[1]).toBe('invalid symbol'); + expect(error.length).toBe(2); + }); + + it('throws an error when an invalid timezome is set', async () => { + console.log('Testing "invalid timezone" error:'); + + const client = new TradingView.Client(); + const chart = new client.Session.Chart(); + chart.setMarket('BINANCE:BTCEUR'); + + // @ts-expect-error + chart.setTimezone('Nowhere/Nowhere'); + + const error = await waitForError(chart); + console.log('=> Chart error:', error); + + expect(error).toBeDefined(); + expect(error[0]).toBe('Critical error:'); + expect(error[1]).toBe('invalid timezone'); + expect(error[2]).toBe('method: switch_timezone. args: "[Nowhere/Nowhere]"'); + expect(error.length).toBe(3); + }); + + it('throws an error when a custom timeframe is set without premium', async () => { + console.log('Testing "custom timeframe" error:'); + + const client = new TradingView.Client(); + const chart = new client.Session.Chart(); + + chart.setMarket('BINANCE:BTCEUR', { // Set a market + // @ts-expect-error + timeframe: '20', // Set a custom timeframe + /* + Timeframe '20' isn't available because we are + not logged in as a premium TradingView account + */ + }); + + const error = await waitForError(chart); + console.log('=> Chart error:', error); + + expect(error).toBeDefined(); + expect(error[0]).toBe('Series error:'); + expect(error[1]).toBe('custom_resolution'); + expect(error.length).toBe(2); + }); + + it('throws an error when an invalid timeframe is set', async () => { + console.log('Testing "Invalid timeframe" error:'); + + const client = new TradingView.Client(); + const chart = new client.Session.Chart(); + + chart.setMarket('BINANCE:BTCEUR', { // Set a market + // @ts-expect-error + timeframe: 'XX', // Set an invalid timeframe + }); + + const error = await waitForError(chart); + console.log('=> Chart error:', error); + + expect(error).toBeDefined(); + expect(error[0]).toBe('Critical error:'); + expect(error[1]).toBe('invalid parameters'); + expect(error[2]).toBe('method: create_series. args: "[$prices, s1, ser_1, XX, 100]"'); + expect(error.length).toBe(3); + }); + + it('throws an error when a premium chart type is set without premium', async () => { + console.log('Testing "Study not auth" error:'); + + const client = new TradingView.Client(); + const chart = new client.Session.Chart(); + + chart.setMarket('BINANCE:BTCEUR', { // Set a market + timeframe: '15', + type: 'Renko', + }); + + const error = await waitForError(chart); + console.log('=> Chart error:', error); + + expect(error).toBeDefined(); + expect(error[0]).toBe('Series error:'); + expect(error[1]).toMatch(/study_not_auth:BarSetRenko@tv-prostudies-\d+/); + expect(error.length).toBe(2); + }); + + it('throws an error when series is edited before market is set', async () => { + console.log('Testing "Set the market before..." error:'); + + const client = new TradingView.Client(); + const chart = new client.Session.Chart(); + + setImmediate(() => { + chart.setSeries('15'); + }); + + const error = await waitForError(chart); + console.log('=> Chart error:', error); + + expect(error).toBeDefined(); + expect(error[0]).toBe('Please set the market before setting series'); + expect(error.length).toBe(1); + }); + + it('throws an error when getting a non-existent indicator', async () => { + console.log('Testing "Inexistent indicator" error:'); + + expect( + TradingView.getIndicator('STD;XXXXXXX'), + ).rejects.toThrow('Inexistent or unsupported indicator: "undefined"'); + }); + + it('throws an error when setting an invalid study option value', async () => { + console.log('Testing "Invalid value" error:'); + + const client = new TradingView.Client(); + const chart = new client.Session.Chart(); + + chart.setMarket('BINANCE:BTCEUR'); // Set a market + + const ST = await TradingView.getIndicator('STD;Supertrend'); + ST.setOption('Factor', -1); // This will cause an error + + const Supertrend = new chart.Study(ST); + + const error = await waitForError(Supertrend) as any; + console.log('=> Study error:', error); + + expect(error).toEqual([ + { + ctx: { + length: -1, + nameInvalidValue: 'factor', + bar_index: 0, + operation: '>', + funName: '\'supertrend\'', + }, + error: 'Error on bar {bar_index}: Invalid value of the \'{nameInvalidValue}\' argument ({length}) in the \'{funName}\' function. It must be {operation} 0.', + }, + 'undefined', + ]); + + console.log('OK'); + }); + + it.skipIf( + !token || !signature, + )('throws an error when getting user data without signature', async () => { + console.log('Testing "Wrong or expired sessionid/signature" error using getUser method:'); + + console.log('Trying with signaure'); + const userInfo = await TradingView.getUser(token, signature); + + console.log('Result:', { + id: userInfo.id, + username: userInfo.username, + firstName: userInfo.firstName, + lastName: userInfo.lastName, + following: userInfo.following, + followers: userInfo.followers, + notifications: userInfo.notifications, + joinDate: userInfo.joinDate, + }); + + expect(userInfo).toBeDefined(); + expect(userInfo.id).toBeDefined(); + + console.log('Trying without signaure'); + expect( + TradingView.getUser(token), + ).rejects.toThrow('Wrong or expired sessionid/signature'); + + console.log('OK'); + }); + + it.skipIf( + !token || !signature, + )('throws an error when creating an authenticated client without signature', async () => { + console.log('Testing "Wrong or expired sessionid/signature" error using client:'); + + const client = new TradingView.Client({ token }); + + const error = await waitForError(client); + console.log('=> Client error:', error); + + expect(error).toBeDefined(); + expect(error[0]).toBe('Credentials error:'); + expect(error[1]).toBe('Wrong or expired sessionid/signature'); + expect(error.length).toBe(2); + }); +}); diff --git a/tests/authenticated.test.ts b/tests/authenticated.test.ts new file mode 100644 index 0000000..dd966c9 --- /dev/null +++ b/tests/authenticated.test.ts @@ -0,0 +1,101 @@ +import { describe, it, expect } from 'vitest'; +import TradingView from '../main'; +import utils from './utils'; + +const token = process.env.SESSION as string; +const signature = process.env.SIGNATURE as string; + +describe.skipIf(!token || !signature)('Authenticated actions', () => { + it('gets user info', async () => { + console.log('Testing getUser method'); + + const userInfo = await TradingView.getUser(token, signature); + + console.log('User:', { + id: userInfo.id, + username: userInfo.username, + firstName: userInfo.firstName, + lastName: userInfo.lastName, + following: userInfo.following, + followers: userInfo.followers, + notifications: userInfo.notifications, + joinDate: userInfo.joinDate, + }); + + expect(userInfo).toBeDefined(); + expect(userInfo.id).toBeDefined(); + expect(userInfo.username).toBeDefined(); + expect(userInfo.firstName).toBeDefined(); + expect(userInfo.lastName).toBeDefined(); + expect(userInfo.following).toBeDefined(); + expect(userInfo.followers).toBeDefined(); + expect(userInfo.notifications).toBeDefined(); + expect(userInfo.notifications.following).toBeDefined(); + expect(userInfo.notifications.user).toBeDefined(); + expect(userInfo.joinDate).toBeDefined(); + + expect(userInfo.session).toBe(token); + expect(userInfo.signature).toBe(signature); + }); + + const userIndicators: any[] = []; + + it('gets user indicators', async () => { + console.log('Testing getPrivateIndicators method'); + + userIndicators.push(...await TradingView.getPrivateIndicators(token)); + console.log('Indicators:', userIndicators.map((i) => i.name)); + + expect(userIndicators.length).toBeGreaterThan(0); + }); + + it('creates a chart with all user indicators', async () => { + console.log('Creating logged client'); + const client = new TradingView.Client({ token, signature }); + const chart = new client.Session.Chart(); + + console.log('Setting market to BINANCE:BTCEUR...'); + chart.setMarket('BINANCE:BTCEUR', { timeframe: 'D' }); + + // Limit to 3 indicators for testing + const testedIndicators = userIndicators.slice(0, 3); + + const checked = new Set(); + async function check(item) { + checked.add(item); + console.log('Checked:', [...checked], `(${checked.size}/${testedIndicators.length + 1})`); + } + + chart.onUpdate(async () => { + console.log('Market data:', { + name: chart.infos.pro_name, + description: chart.infos.short_description, + exchange: chart.infos.exchange, + price: chart.periods[0].close, + }); + + await check(Symbol.for('PRICE')); + }); + + console.log('Loading indicators...'); + for (const indic of testedIndicators) { + const privateIndic = await indic.get(); + console.log(`[${indic.name}] Loading indicator...`); + + const indicator = new chart.Study(privateIndic); + + indicator.onReady(() => { + console.log(`[${indic.name}] Indicator loaded !`); + }); + + indicator.onUpdate(async () => { + console.log(`[${indic.name}] Last plot:`, indicator.periods[0]); + await check(indic.id); + }); + } + + while (checked.size < testedIndicators.length + 1) await utils.wait(100); + + console.log('All indicators loaded !'); + }, 10000); +}); diff --git a/tests/builtInIndicator.test.ts b/tests/builtInIndicator.test.ts new file mode 100644 index 0000000..1c63de3 --- /dev/null +++ b/tests/builtInIndicator.test.ts @@ -0,0 +1,66 @@ +import { describe, it, expect } from 'vitest'; +import TradingView from '../main'; +import utils from './utils'; + +describe('BuiltInIndicator', () => { + let client: TradingView.Client; + let chart: any; + + it('creates a client', async () => { + client = new TradingView.Client(); + expect(client).toBeDefined(); + }); + + it('creates a chart', async () => { + chart = new client.Session.Chart(); + expect(chart).toBeDefined(); + }); + + it('sets market', async () => { + chart.setMarket('BINANCE:BTCEUR', { + timeframe: '60', + }); + + await new Promise((resolve) => { + chart.onSymbolLoaded(() => { + console.log('Chart loaded'); + resolve(true); + }); + }); + + expect(chart.infos.full_name).toBe('BINANCE:BTCEUR'); + }); + + it('gets volume profile', async () => { + const volumeProfile = new TradingView.BuiltInIndicator('VbPFixed@tv-basicstudies-139!'); + volumeProfile.setOption('first_bar_time', Date.now() - 10 ** 8); + + const VOL = new chart.Study(volumeProfile); + + while (!VOL.graphic?.horizHists?.length) await utils.wait(100); + expect(VOL.graphic?.horizHists).toBeDefined(); + + const hists = VOL.graphic.horizHists + .filter((h) => h.lastBarTime === 0) // We only keep recent volume infos + .sort((a, b) => b.priceHigh - a.priceHigh); + + expect(hists.length).toBeGreaterThan(15); + + for (const hist of hists) { + console.log( + `~ ${Math.round((hist.priceHigh + hist.priceLow) / 2)} € :`, + `${'_'.repeat(hist.rate[0] / 3)}${'_'.repeat(hist.rate[1] / 3)}`, + ); + } + }); + + it('removes chart', () => { + console.log('Closing the chart...'); + chart.delete(); + }); + + it('removes client', async () => { + console.log('Closing the client...'); + await client.end(); + }); +}); diff --git a/tests/customChartTypes.test.ts b/tests/customChartTypes.test.ts new file mode 100644 index 0000000..d73c920 --- /dev/null +++ b/tests/customChartTypes.test.ts @@ -0,0 +1,166 @@ +import { describe, it, expect } from 'vitest'; +import TradingView from '../main'; +import utils from './utils'; + +describe('CustomChartTypes', () => { + let client: TradingView.Client; + let chart: any; + + it('creates a client', async () => { + client = new TradingView.Client(); + expect(client).toBeDefined(); + }); + + it('creates a chart', async () => { + chart = new client.Session.Chart(); + expect(chart).toBeDefined(); + }); + + it('sets chart type to HeikinAshi', async () => { + console.log('Setting chart type to: HeikinAshi'); + + chart.setMarket('BINANCE:BTCEUR', { + type: 'HeikinAshi', + timeframe: 'D', + }); + + while ( + chart.infos.full_name !== 'BINANCE:BTCEUR' + || !chart.periods.length + ) await utils.wait(100); + + expect(chart.infos.full_name).toBe('BINANCE:BTCEUR'); + expect(chart.periods.length).toBe(100); + expect( + utils.calculateTimeGap(chart.periods), + ).toBe(86400); + }); + + it('sets chart type to Renko', async () => { + console.log('Setting chart type to: Renko'); + + chart.setMarket('BINANCE:ETHEUR', { + type: 'Renko', + timeframe: 'D', + inputs: { + source: 'close', + sources: 'Close', + boxSize: 3, + style: 'ATR', + atrLength: 14, + wicks: true, + }, + }); + + while ( + chart.infos.full_name !== 'BINANCE:ETHEUR' + || !chart.periods.length + ) await utils.wait(100); + + expect(chart.infos.full_name).toBe('BINANCE:ETHEUR'); + }); + + it('sets chart type to LineBreak', async () => { + console.log('Setting chart type to: LineBreak'); + + chart.setMarket('BINANCE:BTCEUR', { + type: 'LineBreak', + timeframe: 'D', + inputs: { + source: 'close', + lb: 3, + }, + }); + + while ( + chart.infos.full_name !== 'BINANCE:BTCEUR' + || !chart.periods.length + ) await utils.wait(100); + + expect(chart.infos.full_name).toBe('BINANCE:BTCEUR'); + }); + + it('sets chart type to Kagi', async () => { + console.log('Setting chart type to: Kagi'); + + chart.setMarket('BINANCE:ETHEUR', { + type: 'Kagi', + timeframe: 'D', + inputs: { + source: 'close', + style: 'ATR', + atrLength: 14, + reversalAmount: 1, + }, + }); + + while ( + chart.infos.full_name !== 'BINANCE:ETHEUR' + || !chart.periods.length + ) await utils.wait(100); + + expect(chart.infos.full_name).toBe('BINANCE:ETHEUR'); + expect(chart.periods.length).toBe(100); + }); + + it('sets chart type to PointAndFigure', async () => { + console.log('Setting chart type to: PointAndFigure'); + + chart.setMarket('BINANCE:BTCEUR', { + type: 'PointAndFigure', + timeframe: 'D', + inputs: { + sources: 'Close', + reversalAmount: 3, + boxSize: 1, + style: 'ATR', + atrLength: 14, + oneStepBackBuilding: false, + }, + }); + + while ( + chart.infos.full_name !== 'BINANCE:BTCEUR' + || !chart.periods.length + ) await utils.wait(100); + + expect(chart.infos.full_name).toBe('BINANCE:BTCEUR'); + expect(chart.periods.length).toBeGreaterThan(0); + }); + + it('sets chart type to Range', async () => { + console.log('Setting chart type to: Range'); + + chart.setMarket('BINANCE:ETHEUR', { + type: 'Range', + timeframe: 'D', + inputs: { + range: 1, + phantomBars: false, + }, + }); + + while ( + chart.infos.full_name !== 'BINANCE:ETHEUR' + || !chart.periods.length + ) await utils.wait(100); + + expect(chart.infos.full_name).toBe('BINANCE:ETHEUR'); + expect(chart.periods.length).toBeGreaterThanOrEqual(100); + expect(chart.periods.length).toBeLessThanOrEqual(101); + }); + + it('closes chart', async () => { + console.log('Waiting 1 second...'); + await utils.wait(1000); + console.log('Closing the chart...'); + chart.delete(); + }); + + it('closes client', async () => { + console.log('Waiting 1 second...'); + await utils.wait(1000); + console.log('Closing the client...'); + await client.end(); + }); +}); diff --git a/tests/indicators.test.ts b/tests/indicators.test.ts new file mode 100644 index 0000000..600b051 --- /dev/null +++ b/tests/indicators.test.ts @@ -0,0 +1,138 @@ +import { describe, it, expect } from 'vitest'; +import TradingView from '../main'; + +describe('Indicators', () => { + const indicators: { [name: string]: TradingView.PineIndicator } = {}; + + it('gets Supertrend strategy', async () => { + indicators.SuperTrend = await TradingView.getIndicator('STD;Supertrend%Strategy'); + expect(indicators.SuperTrend).toBeDefined(); + expect(indicators.SuperTrend.description).toBe('Supertrend Strategy'); + + indicators.SuperTrend.setOption('commission_type', 'percent'); + indicators.SuperTrend.setOption('commission_value', 0); + indicators.SuperTrend.setOption('initial_capital', 25000); + indicators.SuperTrend.setOption('default_qty_value', 20); + indicators.SuperTrend.setOption('default_qty_type', 'percent_of_equity'); + indicators.SuperTrend.setOption('currency', 'EUR'); + indicators.SuperTrend.setOption('pyramiding', 10); + + expect(indicators.SuperTrend).toBeDefined(); + }); + + it('gets MarketCipher B study', async () => { + indicators.CipherB = await TradingView.getIndicator('PUB;uA35GeckoTA2EfgI63SD2WCSmca4njxp'); + expect(indicators.CipherB).toBeDefined(); + expect(indicators.CipherB.description).toBe('VuManChu B Divergences'); + + indicators.CipherB.setOption('Show_WT_Hidden_Divergences', true); + indicators.CipherB.setOption('Show_Stoch_Regular_Divergences', true); + indicators.CipherB.setOption('Show_Stoch_Hidden_Divergences', true); + + expect(indicators.CipherB).toBeDefined(); + }); + + let client: TradingView.Client; + let chart: any; + + it('creates a client', async () => { + client = new TradingView.Client(); + expect(client).toBeDefined(); + }); + + it('creates a chart', async () => { + chart = new client.Session.Chart(); + expect(chart).toBeDefined(); + }); + + it('sets market', async () => { + chart.setMarket('BINANCE:BTCEUR', { + timeframe: '60', + }); + + await new Promise((resolve) => { + chart.onSymbolLoaded(() => { + console.log('Chart loaded'); + resolve(true); + }); + }); + + expect(chart.infos.full_name).toBe('BINANCE:BTCEUR'); + }); + + it.concurrent('gets performance data from SuperTrend strategy', async () => { + const SuperTrend = new chart.Study(indicators.SuperTrend); + + let QTY = 10; + const perfResult = await new Promise((resolve) => { + SuperTrend.onUpdate(() => { + // SuperTrend is a strategy so it sends a strategy report + const perfReport = SuperTrend.strategyReport.performance; + + console.log('Performances:', { + total: { + trades: perfReport.all.totalTrades, + perf: `${Math.round(perfReport.all.netProfitPercent * 10000) / 100} %`, + }, + buy: { + trades: perfReport.long.totalTrades, + perf: `${Math.round(perfReport.long.netProfitPercent * 10000) / 100} %`, + }, + sell: { + trades: perfReport.short.totalTrades, + perf: `${Math.round(perfReport.short.netProfitPercent * 10000) / 100} %`, + }, + }); + + if (QTY >= 50) { + resolve(true); + return; + } + + QTY += 10; + console.log('TRY WITH', QTY, '%'); + setTimeout(() => { + indicators.SuperTrend.setOption('default_qty_value', QTY); + SuperTrend.setIndicator(indicators.SuperTrend); + }, 1000); + }); + }); + + expect(perfResult).toBe(true); + + SuperTrend.remove(); + }, 10000); + + it.concurrent('gets data from MarketCipher B study', async () => { + const CipherB = new chart.Study(indicators.CipherB); + + const lastResult: any = await new Promise((resolve) => { + CipherB.onUpdate(() => { + resolve(CipherB.periods[0]); + }); + }); + + console.log('MarketCipher B last values:', { + VWAP: Math.round(lastResult.VWAP * 1000) / 1000, + moneyFlow: (lastResult.RSIMFI_Area >= 0) ? 'POSITIVE' : 'NEGATIVE', + buyCircle: lastResult.Buy_and_sell_circle && lastResult.VWAP > 0, + sellCircle: lastResult.Buy_and_sell_circle && lastResult.VWAP < 0, + }); + + expect(lastResult.VWAP).toBeTypeOf('number'); + expect(lastResult.RSIMFI_Area).toBeTypeOf('number'); + expect(lastResult.Buy_and_sell_circle).toBeTypeOf('number'); + + CipherB.remove(); + }); + + it('removes chart', () => { + console.log('Closing the chart...'); + chart.delete(); + }); + + it('removes client', async () => { + console.log('Closing the client...'); + await client.end(); + }); +}); diff --git a/tests/quoteSession.test.ts b/tests/quoteSession.test.ts new file mode 100644 index 0000000..80e4bd7 --- /dev/null +++ b/tests/quoteSession.test.ts @@ -0,0 +1,62 @@ +import { describe, it, expect } from 'vitest'; +import TradingView from '../main'; + +describe('Quote session', () => { + let client: TradingView.Client; + let quoteSession: any; + let BTC: any; + + it('creates client', () => { + client = new TradingView.Client(); + expect(client).toBeDefined(); + }); + + it('creates quote session', () => { + quoteSession = new client.Session.Quote({ fields: 'all' }); + expect(quoteSession).toBeDefined(); + }); + + it('asks for market BTCEUR', () => { + BTC = new quoteSession.Market('BTCEUR'); + expect(BTC).toBeDefined(); + + expect(new Promise((resolve) => { + BTC.onLoaded(() => { + console.log('BTCEUR quote loaded'); + resolve(true); + }); + })).resolves.toBe(true); + }); + + it('data has all properties', () => { + expect(new Promise((resolve) => { + BTC.onData((data) => { + const rsKeys = Object.keys(data); + console.log('BTCEUR data received'); + if (rsKeys.length <= 2) return; + resolve(rsKeys.sort()); + }); + })).resolves.toEqual( + [ + 'ask', 'bid', 'format', + 'volume', 'update_mode', 'type', 'timezone', + 'short_name', 'rtc_time', 'rtc', 'rchp', 'ch', + 'rch', 'provider_id', 'pro_name', 'pricescale', + 'prev_close_price', 'original_name', 'lp', + 'open_price', 'minmove2', 'minmov', 'lp_time', + 'low_price', 'is_tradable', 'high_price', + 'fractional', 'exchange', 'description', + 'current_session', 'currency_code', 'chp', + 'currency-logoid', 'base-currency-logoid', + ].sort(), + ); + }); + + it('closes quote session', () => { + BTC.close(); + }); + + it('closes client', () => { + client.end(); + }); +}); diff --git a/tests/replayMode.test.ts b/tests/replayMode.test.ts new file mode 100644 index 0000000..dc212bb --- /dev/null +++ b/tests/replayMode.test.ts @@ -0,0 +1,124 @@ +import { describe, it, expect } from 'vitest'; +import TradingView from '../main'; +import utils from './utils'; + +describe('ReplayMode', () => { + let client: TradingView.Client; + let chart: any; + + it('creates a client', async () => { + client = new TradingView.Client(); + expect(client).toBeDefined(); + }); + + it('creates a chart', async () => { + chart = new client.Session.Chart(); + expect(chart).toBeDefined(); + }); + + it('sets market', async () => { + chart.setMarket('BINANCE:BTCEUR', { + timeframe: 'D', + replay: Math.round(Date.now() / 1000) - 86400 * 10, + range: 1, + }); + + await new Promise((resolve) => { + chart.onSymbolLoaded(() => { + console.log('Chart loaded'); + resolve(true); + }); + }); + + expect(chart.infos.full_name).toBe('BINANCE:BTCEUR'); + }); + + it('steps forward manually', async () => { + let finished = false; + + async function step() { + if (finished) return; + await chart.replayStep(1); + await utils.wait(100); + console.log('Replay step'); + await step(); + } + step(); + + chart.onReplayPoint((p: number) => { + console.log('Last point ->', p); + }); + + chart.onReplayEnd(async () => { + console.log('Replay end'); + finished = true; + }); + + await new Promise((resolve) => { + chart.onReplayEnd(() => { + finished = true; + resolve(true); + }); + }); + + expect( + utils.calculateTimeGap(chart.periods), + ).toBe(24 * 60 * 60); + + expect(chart.periods.length).toBe(11); + }); + + it('sets market', async () => { + chart.setMarket('BINANCE:BTCEUR', { + timeframe: 'D', + replay: Math.round(Date.now() / 1000) - 86400 * 10, + range: 1, + }); + + await new Promise((resolve) => { + chart.onSymbolLoaded(() => { + console.log('Chart loaded'); + resolve(true); + }); + }); + + expect(chart.infos.full_name).toBe('BINANCE:BTCEUR'); + }); + + it('steps forward automatically', async () => { + console.log('Play replay mode'); + await chart.replayStart(200); + + chart.onUpdate(() => { + console.log('Point ->', chart.periods[0].time); + }); + + await new Promise((resolve) => { + chart.onReplayEnd(() => { + console.log('Replay end'); + resolve(true); + }); + }); + + expect( + utils.calculateTimeGap(chart.periods), + ).toBe(24 * 60 * 60); + + expect(chart.periods.length).toBeGreaterThanOrEqual(10); + expect(chart.periods.length).toBeLessThanOrEqual(11); + }); + + it('closes chart', async () => { + console.log('Waiting 1 second...'); + await utils.wait(1000); + console.log('Closing the chart...'); + chart.delete(); + }); + + it('closes client', async () => { + console.log('Waiting 1 second...'); + await utils.wait(1000); + console.log('Closing the client...'); + await client.end(); + }); +}); diff --git a/tests/search.test.ts b/tests/search.test.ts new file mode 100644 index 0000000..0ad8bac --- /dev/null +++ b/tests/search.test.ts @@ -0,0 +1,40 @@ +import { describe, it, expect } from 'vitest'; +import { searchMarket, searchIndicator } from '../main'; + +describe('Search functions', () => { + it('market search: "BINANCE:" has results', async () => { + console.log('Searching market: "BINANCE:"'); + const markets = await searchMarket('BINANCE:'); + console.log('Found', markets.length, 'markets'); + + expect(markets.length).toBeGreaterThan(10); + }); + + it('indicator search: "RSI" has results', async () => { + console.log('Searching indicator: "RSI"'); + const indicators = await searchIndicator('RSI'); + console.log('Found', indicators.length, 'indicators'); + + expect(indicators.length).toBeGreaterThan(10); + }); +}); + +describe('Technical Analysis', () => { + function checkTA(ta = {}) { + expect(ta).toBeDefined(); + for (const period of ['1', '5', '15', '60', '240', '1D', '1W', '1M']) { + expect(ta[period]).toBeDefined(); + expect(ta[period].Other).toBeDefined(); + expect(ta[period].All).toBeDefined(); + expect(ta[period].MA).toBeDefined(); + } + } + + it('gets TA for BINANCE:BTCUSD', async () => { + checkTA(await (await searchMarket('BINANCE:BTCUSD'))[0].getTA()); + }); + + it('gets TA for NASDAQ:AAPL', async () => { + checkTA(await (await searchMarket('NASDAQ:AAPL'))[0].getTA()); + }); +}); diff --git a/tests/simpleChart.test.ts b/tests/simpleChart.test.ts new file mode 100644 index 0000000..1649eec --- /dev/null +++ b/tests/simpleChart.test.ts @@ -0,0 +1,79 @@ +import { describe, it, expect } from 'vitest'; +import TradingView from '../main'; +import utils from './utils'; + +describe('Simple chart session', async () => { + let client: TradingView.Client; + let chart: any; + + it('creates a client', () => { + client = new TradingView.Client(); + expect(client).toBeDefined(); + }); + + it('creates a chart session', () => { + chart = new client.Session.Chart(); + expect(chart).toBeDefined(); + }); + + it('sets market', async () => { + chart.setMarket('BINANCE:BTCEUR', { + timeframe: 'D', + }); + + while ( + chart.infos.full_name !== 'BINANCE:BTCEUR' + || !chart.periods.length + ) await utils.wait(100); + + expect(chart.infos.full_name).toBe('BINANCE:BTCEUR'); + expect( + utils.calculateTimeGap(chart.periods), + ).toBe(24 * 60 * 60); + }); + + it('sets timeframe', async () => { + console.log('Waiting 1 second...'); + await utils.wait(1000); + + console.log('Setting timeframe to 15 minutes...'); + chart.setSeries('15'); + + while (!chart.periods.length) await utils.wait(100); + console.log('Chart timeframe set'); + + expect( + utils.calculateTimeGap(chart.periods), + ).toBe(15 * 60); + }); + + it('sets chart type', async () => { + console.log('Waiting 1 second...'); + await utils.wait(1000); + + console.log('Setting the chart type to "Heikin Ashi"...'); + chart.setMarket('BINANCE:ETHEUR', { + timeframe: 'D', + type: 'HeikinAshi', + }); + + while (chart.infos.full_name !== 'BINANCE:ETHEUR') await utils.wait(100); + + console.log('Chart type set'); + expect(chart.infos.full_name).toBe('BINANCE:ETHEUR'); + }); + + it('closes chart', async () => { + console.log('Waiting 1 second...'); + await utils.wait(1000); + console.log('Closing the chart...'); + chart.delete(); + }); + + it('closes client', async () => { + console.log('Waiting 1 second...'); + await utils.wait(1000); + console.log('Closing the client...'); + await client.end(); + }); +}); diff --git a/tests/utils.ts b/tests/utils.ts new file mode 100644 index 0000000..9ecd81a --- /dev/null +++ b/tests/utils.ts @@ -0,0 +1,23 @@ +export const wait = (ms: number) => ( + new Promise((resolve) => { setTimeout(resolve, ms); }) +); + +export function calculateTimeGap(periods: { time: number }[]) { + let minTimeGap = Infinity; + + for (let i = 1; i < 10; i += 1) { + const time1 = periods[i - 1]?.time; + const time2 = periods[i]?.time; + if (!time1 || !time2) continue; + + const timeGap = time1 - time2; + if (timeGap < minTimeGap) minTimeGap = timeGap; + } + + return minTimeGap; +} + +export default { + wait, + calculateTimeGap, +}; diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..981eeeb --- /dev/null +++ b/vite.config.js @@ -0,0 +1,6 @@ +module.exports = { + test: { + testTimeout: 10000, + setupFiles: 'dotenv/config', + }, +};