diff options
35 files changed, 2190 insertions, 2426 deletions
diff --git a/LICENSE.txt b/LICENSE.txt index a169586..6db2c01 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2023 Vaughn Nugent +Copyright (C) 2024 Vaughn Nugent Contact information Name: Vaughn Nugent diff --git a/front-end/package-lock.json b/front-end/package-lock.json index 3b684ae..8123f03 100644 --- a/front-end/package-lock.json +++ b/front-end/package-lock.json @@ -9,7 +9,6 @@ "version": "0.1.2", "license": "agpl3", "dependencies": { - "@chenfengyuan/vue-qrcode": "^2.0.0", "@fontsource/source-sans-pro": "^5.0.8", "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0", @@ -18,7 +17,7 @@ "@headlessui/vue": "^1.7.12", "@kyvg/vue3-notification": "^3.0.x", "@vnuge/cmnext-admin": "../lib/admin", - "@vnuge/vnlib.browser": "F:\\Programming\\VNLib\\Plugins\\Essentials\\lib\\vnlib.browser", + "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/df7dc615532d3441f527374d18664c1a5a336de6/@vnuge-vnlib.browser/release.tgz", "@vuelidate/core": "^2.0.2", "@vuelidate/validators": "^2.0.2", "@vueuse/core": "^10.3.x", @@ -30,6 +29,7 @@ "lodash-es": "^4.17.21", "otpauth": "^9.1.2", "pinia": "^2.1.7", + "qrcode.vue": "^3.4.1", "showdown": "^2.1.0", "universal-cookie": "^7.0.x", "vue": "^3.2.47", @@ -41,7 +41,6 @@ "@types/lodash-es": "^4.14.194", "@types/showdown": "^2.0.1", "@vitejs/plugin-vue": "^5.0.x", - "@volar-plugins/vetur": "latest", "autoprefixer": "^10.4.14", "dotenv": "^16.0.3", "postcss": "^8.4.23", @@ -55,28 +54,7 @@ "vue-tsc": "^1.4.2" } }, - "../../VNLib/Plugins/Essentials/lib/vnlib.browser": { - "version": "0.1.13", - "license": "MIT", - "devDependencies": { - "@babel/types": "^7.x", - "@types/lodash-es": "^4.14.x", - "@types/node": "^20.5.1", - "@typescript-eslint/eslint-plugin": "^6.x.x" - }, - "peerDependencies": { - "@vueuse/core": "^10.x", - "axios": "^1.x", - "eslint": "^8.39.0", - "jose": "^5.x", - "lodash-es": "^4.x", - "universal-cookie": "^7.0.x", - "vue": "^3.x", - "vue-router": "^4.x" - } - }, "../lib/admin": { - "name": "@vnuge/cmnext-admin", "version": "0.1.2", "license": "agpl3", "devDependencies": { @@ -85,7 +63,7 @@ "@typescript-eslint/eslint-plugin": "^6.4.x" }, "peerDependencies": { - "@vnuge/vnlib.browser": "F:\\Programming\\VNLib\\Plugins\\Essentials\\lib\\vnlib.browser", + "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/df7dc615532d3441f527374d18664c1a5a336de6/@vnuge-vnlib.browser/release.tgz", "@vueuse/core": "^10.x", "axios": "^1.x", "jose": "^5.1.x", @@ -98,7 +76,7 @@ "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -127,90 +105,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", @@ -229,95 +123,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "bin": { "parser": "bin/babel-parser.js" }, @@ -326,9 +135,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", - "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", @@ -339,15 +148,6 @@ "node": ">=6.9.0" } }, - "node_modules/@chenfengyuan/vue-qrcode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@chenfengyuan/vue-qrcode/-/vue-qrcode-2.0.0.tgz", - "integrity": "sha512-33Cfr0zjbc3Dd8d5b1IgzXRAgXH0c2Gv19VI4snS25V/x9Z41eg769tC+Us1x+vqgQQhgD5YUjLnkpkrQfeMSw==", - "peerDependencies": { - "qrcode": "^1.5.0", - "vue": "^3.0.0" - } - }, "node_modules/@ckeditor/ckeditor5-core": { "version": "40.2.0", "resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-core/-/ckeditor5-core-40.2.0.tgz", @@ -404,9 +204,9 @@ } }, "node_modules/@codemirror/autocomplete": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.11.1.tgz", - "integrity": "sha512-L5UInv8Ffd6BPw0P3EF7JLYAMeEbclY7+6Q11REt8vhih8RuLreKtPy/xk8wPxs4EQgYqzI7cdgpiYwWlbS/ow==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.12.0.tgz", + "integrity": "sha512-r4IjdYFthwbCQyvqnSlx0WBHRHi8nBvU+WjJxFUij81qsBfhNudf/XKKmmC2j3m0LaOYUQTf3qiEK1J8lO1sdg==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -479,9 +279,9 @@ "integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==" }, "node_modules/@codemirror/view": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.0.tgz", - "integrity": "sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ==", + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.1.tgz", + "integrity": "sha512-J2Xnn5lFYT1ZN/5ewEoMBCmLlL71lZ3mBdb7cUEuHhX2ESoSrNEucpsDXpX22EuTGm9LOgC9v4Z0wx+Ez8QmGA==", "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", @@ -489,9 +289,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", - "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", "cpu": [ "ppc64" ], @@ -505,9 +305,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz", - "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", "cpu": [ "arm" ], @@ -521,9 +321,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz", - "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", "cpu": [ "arm64" ], @@ -537,9 +337,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz", - "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", "cpu": [ "x64" ], @@ -553,9 +353,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", - "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", "cpu": [ "arm64" ], @@ -569,9 +369,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz", - "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", "cpu": [ "x64" ], @@ -585,9 +385,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz", - "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", "cpu": [ "arm64" ], @@ -601,9 +401,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz", - "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", "cpu": [ "x64" ], @@ -617,9 +417,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz", - "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", "cpu": [ "arm" ], @@ -633,9 +433,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz", - "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", "cpu": [ "arm64" ], @@ -649,9 +449,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz", - "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", "cpu": [ "ia32" ], @@ -665,9 +465,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz", - "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", "cpu": [ "loong64" ], @@ -681,9 +481,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz", - "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", "cpu": [ "mips64el" ], @@ -697,9 +497,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz", - "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", "cpu": [ "ppc64" ], @@ -713,9 +513,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz", - "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "cpu": [ "riscv64" ], @@ -729,9 +529,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz", - "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "cpu": [ "s390x" ], @@ -745,9 +545,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz", - "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "cpu": [ "x64" ], @@ -761,9 +561,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz", - "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "cpu": [ "x64" ], @@ -777,9 +577,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz", - "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "cpu": [ "x64" ], @@ -793,9 +593,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz", - "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", "cpu": [ "x64" ], @@ -809,9 +609,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz", - "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "cpu": [ "arm64" ], @@ -825,9 +625,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz", - "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "cpu": [ "ia32" ], @@ -841,9 +641,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", - "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "cpu": [ "x64" ], @@ -860,7 +660,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, + "peer": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -875,7 +675,7 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, + "peer": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -884,7 +684,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, + "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -907,7 +707,7 @@ "version": "8.56.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", - "dev": true, + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -975,9 +775,9 @@ } }, "node_modules/@fortawesome/vue-fontawesome": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.5.tgz", - "integrity": "sha512-isZZ4+utQH9qg9cWxWYHQ9GwI3r5FeO7GnmzKYV+gbjxcptQhh+F99iZXi1Y9AvFUEgy8kRpAdvDlbb3drWFrw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.6.tgz", + "integrity": "sha512-akrL7lTroyNpPkoHtvK2UpsMzJr6jXdHaQ0YdcwqDsB8jdwlpNHZYijpOUd9KJsARr+VB3WXY4EyObepqJ4ytQ==", "peerDependencies": { "@fortawesome/fontawesome-svg-core": "~1 || ~6", "vue": ">= 3.0.0 < 4" @@ -998,13 +798,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "peer": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -1015,7 +815,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, + "peer": true, "engines": { "node": ">=12.22" }, @@ -1025,10 +825,10 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "peer": true }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -1059,41 +859,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -1109,23 +874,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -1161,9 +909,9 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1178,9 +926,9 @@ } }, "node_modules/@lezer/common": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.0.tgz", - "integrity": "sha512-Wmvlm4q6tRpwiy20TnB3yyLTZim38Tkc50dPY8biQRwqE+ati/wD84rm3N15hikvdT4uSg9phs9ubjvcLmkpKg==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" }, "node_modules/@lezer/highlight": { "version": "1.2.0", @@ -1201,9 +949,9 @@ } }, "node_modules/@lezer/lr": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", - "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", + "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", "dependencies": { "@lezer/common": "^1.0.0" } @@ -1212,7 +960,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1225,7 +972,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -1234,7 +980,6 @@ "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, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1286,9 +1031,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.4.tgz", - "integrity": "sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz", + "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==", "cpu": [ "arm" ], @@ -1299,9 +1044,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.4.tgz", - "integrity": "sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz", + "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==", "cpu": [ "arm64" ], @@ -1312,9 +1057,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.4.tgz", - "integrity": "sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz", + "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==", "cpu": [ "arm64" ], @@ -1325,9 +1070,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.4.tgz", - "integrity": "sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz", + "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==", "cpu": [ "x64" ], @@ -1338,9 +1083,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.4.tgz", - "integrity": "sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz", + "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==", "cpu": [ "arm" ], @@ -1351,9 +1096,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.4.tgz", - "integrity": "sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz", + "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==", "cpu": [ "arm64" ], @@ -1364,9 +1109,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.4.tgz", - "integrity": "sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz", + "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==", "cpu": [ "arm64" ], @@ -1377,9 +1122,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.4.tgz", - "integrity": "sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz", + "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==", "cpu": [ "riscv64" ], @@ -1390,9 +1135,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.4.tgz", - "integrity": "sha512-dIYgo+j1+yfy81i0YVU5KnQrIJZE8ERomx17ReU4GREjGtDW4X+nvkBak2xAUpyqLs4eleDSj3RrV72fQos7zw==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz", + "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==", "cpu": [ "x64" ], @@ -1403,9 +1148,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.4.tgz", - "integrity": "sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz", + "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==", "cpu": [ "x64" ], @@ -1416,9 +1161,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.4.tgz", - "integrity": "sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz", + "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==", "cpu": [ "arm64" ], @@ -1429,9 +1174,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.4.tgz", - "integrity": "sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz", + "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==", "cpu": [ "ia32" ], @@ -1442,9 +1187,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.4.tgz", - "integrity": "sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz", + "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==", "cpu": [ "x64" ], @@ -1469,9 +1214,9 @@ } }, "node_modules/@tanstack/vue-virtual": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.0.1.tgz", - "integrity": "sha512-85Cyi8m7h1xzGB2FyXMurPVFOZvatycVU7OfhQ8QFk27E4tQ7ISNfYEMrakTTaE0ZyNsKRFlAzHuwL1Bv1vuMw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.0.2.tgz", + "integrity": "sha512-1iFpX+yZswHuf4wrA6GU9yJ/YzQ/8SacABwqghwCkcwrkZbOPLlRSdOAqZ1WQ50SftmfhZpaiZl2KmpV7cgfMQ==", "dependencies": { "@tanstack/virtual-core": "3.0.0" }, @@ -1523,12 +1268,12 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "peer": true }, "node_modules/@vitejs/plugin-vue": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.2.tgz", - "integrity": "sha512-kEjJHrLb5ePBvjD0SPZwJlw1QTRcjjCA9sB5VyfonoXVBxTS7TMnqL6EkLt1Eu61RDeiuZ/WN9Hf6PxXhPI2uA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz", + "integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==", "dev": true, "engines": { "node": "^18.0.0 || >=20.0.0" @@ -1543,26 +1288,19 @@ "link": true }, "node_modules/@vnuge/vnlib.browser": { - "resolved": "../../VNLib/Plugins/Essentials/lib/vnlib.browser", - "link": true - }, - "node_modules/@volar-plugins/vetur": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@volar-plugins/vetur/-/vetur-2.0.0.tgz", - "integrity": "sha512-RsubwRwnv497I8Dtru8gE/C6P+nmvmMLNnQnyLU3njTqO5Dm4KaBr6cbVl8iQOXHOxfBXzI3lmQSPWdsp3dXRA==", - "deprecated": "WARNING: This project has been renamed to volar-service-vetur. Install using volar-service-vetur instead.", - "dev": true, - "dependencies": { - "vls": "^0.8.2", - "vscode-html-languageservice": "^5.0.4" - }, + "version": "0.1.13", + "resolved": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/df7dc615532d3441f527374d18664c1a5a336de6/@vnuge-vnlib.browser/release.tgz", + "integrity": "sha512-Ddm0cVV4GeNaO5Dd8ycMklswnXBY4L+7gOSu2W1vOOabc8YDaP2XsTaA+xImFVLXj86hQbBTRUUKF9Ebacx+WQ==", + "license": "MIT", "peerDependencies": { - "@volar/language-service": "*" - }, - "peerDependenciesMeta": { - "@volar/language-service": { - "optional": true - } + "@vueuse/core": "^10.x", + "axios": "^1.x", + "eslint": "^8.39.0", + "jose": "^5.x", + "lodash-es": "^4.x", + "universal-cookie": "^7.0.x", + "vue": "^3.x", + "vue-router": "^4.x" } }, "node_modules/@volar/language-core": { @@ -1593,21 +1331,15 @@ "path-browserify": "^1.0.1" } }, - "node_modules/@vscode/l10n": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.16.tgz", - "integrity": "sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg==", - "dev": true - }, "node_modules/@vue-macros/common": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-1.10.0.tgz", - "integrity": "sha512-4DZsPeQA/nBQDw2RkYAmH7KrFjJVrMdAhJhO1JCl1bbbFXCGeoGjXfkg9wHPppj47s2HpAB3GrqNwqVGbi12NQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-1.10.1.tgz", + "integrity": "sha512-uftSpfwdwitcQT2lM8aVxcfe5rKQBzC9jMrtJM5sG4hEuFyfIvnJihpPpnaWxY+X4p64k+YYXtBFv+1O5Bq3dg==", "dev": true, "dependencies": { - "@babel/types": "^7.23.5", + "@babel/types": "^7.23.6", "@rollup/pluginutils": "^5.1.0", - "@vue/compiler-sfc": "^3.3.10", + "@vue/compiler-sfc": "^3.4.13", "ast-kit": "^0.11.3", "local-pkg": "^0.5.0", "magic-string-ast": "^0.3.0" @@ -1641,49 +1373,49 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.7.tgz", - "integrity": "sha512-hhCaE3pTMrlIJK7M/o3Xf7HV8+JoNTGOQ/coWS+V+pH6QFFyqtoXqQzpqsNp7UK17xYKua/MBiKj4e1vgZOBYw==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz", + "integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==", "dependencies": { "@babel/parser": "^7.23.6", - "@vue/shared": "3.4.7", + "@vue/shared": "3.4.15", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.7.tgz", - "integrity": "sha512-qDKBAIurCTub4n/6jDYkXwgsFuriqqmmLrIq1N2QDfYJA/mwiwvxi09OGn28g+uDdERX9NaKDLji0oTjE3sScg==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz", + "integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==", "dependencies": { - "@vue/compiler-core": "3.4.7", - "@vue/shared": "3.4.7" + "@vue/compiler-core": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.7.tgz", - "integrity": "sha512-Gec6CLkReVswDYjQFq79O5rktri4R7TsD/VPCiUoJw40JhNNxaNJJa8mrQrWoJluW4ETy6QN0NUyC/JO77OCOw==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz", + "integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==", "dependencies": { "@babel/parser": "^7.23.6", - "@vue/compiler-core": "3.4.7", - "@vue/compiler-dom": "3.4.7", - "@vue/compiler-ssr": "3.4.7", - "@vue/shared": "3.4.7", + "@vue/compiler-core": "3.4.15", + "@vue/compiler-dom": "3.4.15", + "@vue/compiler-ssr": "3.4.15", + "@vue/shared": "3.4.15", "estree-walker": "^2.0.2", "magic-string": "^0.30.5", - "postcss": "^8.4.32", + "postcss": "^8.4.33", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.7.tgz", - "integrity": "sha512-PvYeSOvnCkST5mGS0TLwEn5w+4GavtEn6adcq8AspbHaIr+mId5hp7cG3ASy3iy8b+LuXEG2/QaV/nj5BQ/Aww==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz", + "integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==", "dependencies": { - "@vue/compiler-dom": "3.4.7", - "@vue/shared": "3.4.7" + "@vue/compiler-dom": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/devtools-api": { @@ -1741,48 +1473,48 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.7.tgz", - "integrity": "sha512-F539DO0ogH0+L8F9Pnw7cjqibcmSOh5UTk16u5f4MKQ8fraqepI9zdh+sozPX6VmEHOcjo8qw3Or9ZcFFw4SZA==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz", + "integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==", "dependencies": { - "@vue/shared": "3.4.7" + "@vue/shared": "3.4.15" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.7.tgz", - "integrity": "sha512-QMMsWRQaD3BpGyjjChthpl4Mji4Fjx1qfdufsXlDkKU3HV+hWNor2z+29F+E1MmVcP0ZfRZUfqYgtsQoL7IGwQ==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz", + "integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==", "dependencies": { - "@vue/reactivity": "3.4.7", - "@vue/shared": "3.4.7" + "@vue/reactivity": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.7.tgz", - "integrity": "sha512-XwegyUY1rw8zxsX1Z36vwYcqo+uOgih5ti7y9vx+pPFhNdSQmN4LqK2RmSeAJG1oKV8NqSUmjpv92f/x6h0SeQ==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz", + "integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==", "dependencies": { - "@vue/runtime-core": "3.4.7", - "@vue/shared": "3.4.7", + "@vue/runtime-core": "3.4.15", + "@vue/shared": "3.4.15", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.7.tgz", - "integrity": "sha512-3bWnYLEkLLhkDWqvNk7IvbQD4UcxvFKxELBiOO2iG3m6AniFIsBWfHOO5tLVQnjdWkODu4rq0GipmfEenVAK5Q==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz", + "integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==", "dependencies": { - "@vue/compiler-ssr": "3.4.7", - "@vue/shared": "3.4.7" + "@vue/compiler-ssr": "3.4.15", + "@vue/shared": "3.4.15" }, "peerDependencies": { - "vue": "3.4.7" + "vue": "3.4.15" } }, "node_modules/@vue/shared": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.7.tgz", - "integrity": "sha512-G+i4glX1dMJk88sbJEcQEGWRQnVm9eIY7CcQbO5dpdsD9SF8jka3Mr5OqZYGjczGN1+D6EUwdu6phcmcx9iuPA==" + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz", + "integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==" }, "node_modules/@vuelidate/core": { "version": "2.0.3", @@ -1869,13 +1601,13 @@ } }, "node_modules/@vueuse/core": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.1.tgz", - "integrity": "sha512-74mWHlaesJSWGp1ihg76vAnfVq9NTv1YT0SYhAQ6zwFNdBkkP+CKKJmVOEHcdSnLXCXYiL5e7MaewblfiYLP7g==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.2.tgz", + "integrity": "sha512-AOyAL2rK0By62Hm+iqQn6Rbu8bfmbgaIMXcE3TSr7BdQ42wnSFlwIdPjInO62onYsEMK/yDMU8C6oGfDAtZ2qQ==", "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.7.1", - "@vueuse/shared": "10.7.1", + "@vueuse/metadata": "10.7.2", + "@vueuse/shared": "10.7.2", "vue-demi": ">=0.14.6" }, "funding": { @@ -1908,19 +1640,19 @@ } }, "node_modules/@vueuse/metadata": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.1.tgz", - "integrity": "sha512-jX8MbX5UX067DYVsbtrmKn6eG6KMcXxLRLlurGkZku5ZYT3vxgBjui2zajvUZ18QLIjrgBkFRsu7CqTAg18QFw==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.2.tgz", + "integrity": "sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ==", "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/router": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@vueuse/router/-/router-10.7.1.tgz", - "integrity": "sha512-u34c+oA6hMy7q0U01hNzb9xGo7M9AHB6/F/olbdYJU7bDTKbj1SXGMhBTJuVFTvBoInV0vazrM+pN3PS6Vwhng==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/router/-/router-10.7.2.tgz", + "integrity": "sha512-TE9QtcqtZ793bwJWnlxn1gir7TXHJh5Y0KTIAS2DFOFvz4f3yTPmxAtIHC5e96oBVaVdnzxQQ9EsOhBDfBtCDg==", "dependencies": { - "@vueuse/shared": "10.7.1", + "@vueuse/shared": "10.7.2", "vue-demi": ">=0.14.6" }, "funding": { @@ -1956,9 +1688,9 @@ } }, "node_modules/@vueuse/shared": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.7.1.tgz", - "integrity": "sha512-v0jbRR31LSgRY/C5i5X279A/WQjD6/JsMzGa+eqt658oJ75IvQXAeONmwvEMrvJQKnRElq/frzBR7fhmWY5uLw==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.7.2.tgz", + "integrity": "sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==", "dependencies": { "vue-demi": ">=0.14.6" }, @@ -2006,7 +1738,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -2015,7 +1746,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2077,7 +1808,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "peer": true }, "node_modules/aria-query": { "version": "5.3.0", @@ -2134,9 +1865,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/autoprefixer": { - "version": "10.4.16", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", - "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", "dev": true, "funding": [ { @@ -2153,9 +1884,9 @@ } ], "dependencies": { - "browserslist": "^4.21.10", - "caniuse-lite": "^1.0.30001538", - "fraction.js": "^4.3.6", + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -2171,9 +1902,9 @@ } }, "node_modules/axios": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", - "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", "dependencies": { "follow-redirects": "^1.15.4", "form-data": "^4.0.0", @@ -2181,9 +1912,9 @@ } }, "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", + "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", "dependencies": { "dequal": "^2.0.3" } @@ -2191,8 +1922,7 @@ "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 + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base32-encode": { "version": "2.0.0", @@ -2213,17 +1943,11 @@ "node": ">=8" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2241,9 +1965,9 @@ } }, "node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz", + "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==", "dev": true, "funding": [ { @@ -2260,8 +1984,8 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", + "caniuse-lite": "^1.0.30001580", + "electron-to-chromium": "^1.4.648", "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" }, @@ -2272,42 +1996,10 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "peer": true, "engines": { "node": ">=6" @@ -2323,9 +2015,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001576", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", - "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==", + "version": "1.0.30001581", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz", + "integrity": "sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ==", "dev": true, "funding": [ { @@ -2346,7 +2038,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2358,15 +2050,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/character-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", - "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", - "dev": true, - "dependencies": { - "is-regex": "^1.0.3" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -2404,17 +2087,6 @@ "node": ">= 6" } }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "peer": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, "node_modules/code-red": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", @@ -2436,9 +2108,9 @@ } }, "node_modules/codemirror-wrapped-line-indent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/codemirror-wrapped-line-indent/-/codemirror-wrapped-line-indent-1.0.0.tgz", - "integrity": "sha512-8ny0CSJ1T6mYQuBF7yGzgRRv+zOXUBaBscvJH3jczK7Isi19RA2Ans9ip0gZBkUWCOhFmmw1IC1a+uN4zj0GNg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/codemirror-wrapped-line-indent/-/codemirror-wrapped-line-indent-1.0.3.tgz", + "integrity": "sha512-1MWPgyxcDcpGpqmBlraoQyIgbZMAmppj/e/9+gpqug68Gli+BtSLE3GLxGoRoRK5n5sFp8RH0xAQL5i7jOo2qQ==", "peerDependencies": { "@codemirror/language": "^6.9.0", "@codemirror/state": "^6.2.1", @@ -2499,7 +2171,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "peer": true }, "node_modules/cookie": { "version": "0.6.0", @@ -2518,7 +2190,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2567,7 +2238,6 @@ "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" }, @@ -2580,34 +2250,11 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } + "peer": true }, "node_modules/delayed-stream": { "version": "1.0.0", @@ -2631,15 +2278,6 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "dev": true }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -2648,12 +2286,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dijkstrajs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", - "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", - "peer": true - }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -2664,7 +2296,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -2673,9 +2305,9 @@ } }, "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==", "dev": true, "engines": { "node": ">=12" @@ -2691,21 +2323,16 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.625", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.625.tgz", - "integrity": "sha512-DENMhh3MFgaPDoXWrVIqSPInQoLImywfCwrSmVl3cf9QHzoZSiutHwGaB/Ql3VkqcQV30rzgdM+BjKqBAJxo5Q==", + "version": "1.4.651", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.651.tgz", + "integrity": "sha512-jjks7Xx+4I7dslwsbaFocSwqBbGHQmuXBJUK9QBZTIrzPq3pzn6Uf2szFSP728FtLYE3ldiccmlkOM/zhGKCpA==", "dev": true }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/encode-utf8": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", - "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", - "peer": true + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/entities": { "version": "4.5.0", @@ -2719,9 +2346,9 @@ } }, "node_modules/esbuild": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", - "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", "dev": true, "hasInstallScript": true, "bin": { @@ -2731,29 +2358,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.11", - "@esbuild/android-arm": "0.19.11", - "@esbuild/android-arm64": "0.19.11", - "@esbuild/android-x64": "0.19.11", - "@esbuild/darwin-arm64": "0.19.11", - "@esbuild/darwin-x64": "0.19.11", - "@esbuild/freebsd-arm64": "0.19.11", - "@esbuild/freebsd-x64": "0.19.11", - "@esbuild/linux-arm": "0.19.11", - "@esbuild/linux-arm64": "0.19.11", - "@esbuild/linux-ia32": "0.19.11", - "@esbuild/linux-loong64": "0.19.11", - "@esbuild/linux-mips64el": "0.19.11", - "@esbuild/linux-ppc64": "0.19.11", - "@esbuild/linux-riscv64": "0.19.11", - "@esbuild/linux-s390x": "0.19.11", - "@esbuild/linux-x64": "0.19.11", - "@esbuild/netbsd-x64": "0.19.11", - "@esbuild/openbsd-x64": "0.19.11", - "@esbuild/sunos-x64": "0.19.11", - "@esbuild/win32-arm64": "0.19.11", - "@esbuild/win32-ia32": "0.19.11", - "@esbuild/win32-x64": "0.19.11" + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" } }, "node_modules/escalade": { @@ -2769,7 +2396,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, + "peer": true, "engines": { "node": ">=10" }, @@ -2781,7 +2408,7 @@ "version": "8.56.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", - "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -2832,32 +2459,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-plugin-vue": { - "version": "9.19.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.19.2.tgz", - "integrity": "sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "natural-compare": "^1.4.0", - "nth-check": "^2.1.1", - "postcss-selector-parser": "^6.0.13", - "semver": "^7.5.4", - "vue-eslint-parser": "^9.3.1", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -2873,7 +2478,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2885,7 +2489,6 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -2898,24 +2501,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -2927,7 +2516,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -2939,7 +2527,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -2953,7 +2540,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -2995,19 +2582,18 @@ "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 + "peer": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "peer": true }, "node_modules/fastq": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", - "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", - "dev": true, + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.0.tgz", + "integrity": "sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w==", "dependencies": { "reusify": "^1.0.4" } @@ -3016,7 +2602,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, + "peer": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -3039,7 +2625,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, + "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3055,7 +2641,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, + "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -3069,12 +2655,12 @@ "version": "3.2.9", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true + "peer": true }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "funding": [ { "type": "individual", @@ -3136,7 +2722,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "peer": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -3160,35 +2746,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "peer": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, + "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3208,7 +2770,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -3220,7 +2781,7 @@ "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, + "peer": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -3231,84 +2792,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "peer": true }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "peer": true, "engines": { "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -3334,15 +2832,15 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, + "peer": true, "engines": { "node": ">= 4" } }, "node_modules/immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==" + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==" }, "node_modules/immutable-json-patch": { "version": "5.1.3", @@ -3353,7 +2851,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, + "peer": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3369,7 +2867,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, + "peer": true, "engines": { "node": ">=0.8.19" } @@ -3378,7 +2876,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, + "peer": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3388,7 +2886,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "peer": true }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -3413,28 +2911,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-expression": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", - "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", - "dev": true, - "dependencies": { - "acorn": "^7.1.1", - "object-assign": "^4.1.1" - } - }, - "node_modules/is-expression/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3447,6 +2923,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -3474,7 +2951,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, + "peer": true, "engines": { "node": ">=8" } @@ -3487,27 +2964,10 @@ "@types/estree": "*" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/jackspeak": { "version": "2.3.6", @@ -3552,17 +3012,11 @@ "url": "https://github.com/sponsors/panva" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -3574,7 +3028,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "peer": true }, "node_modules/json-editor-vue": { "version": "0.11.2", @@ -3627,7 +3081,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "peer": true }, "node_modules/json-source-map": { "version": "0.6.1", @@ -3638,7 +3092,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "peer": true }, "node_modules/json5": { "version": "2.2.3", @@ -3653,15 +3107,15 @@ } }, "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==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", "dev": true }, "node_modules/jsonrepair": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.5.0.tgz", - "integrity": "sha512-SavvDsUP9Xnqo2MoC6Wl6zNyX3f+I5199hRbXBtAITyP2NTPyAgyx5xM0bgcIljRjzsIvOBANbgfWe8XXlyeLA==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.5.1.tgz", + "integrity": "sha512-F0VxiEj1j7m1OAVUVy6fFYk5s8tthF61J7tjYtEACw1DeNQqKmZF6dPddduxc7Tc5IrLqKTdLAwUNTmrqqg+hw==", "bin": { "jsonrepair": "bin/cli.js" } @@ -3678,7 +3132,7 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, + "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -3687,7 +3141,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, + "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -3732,7 +3186,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, + "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -3758,7 +3212,7 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "peer": true }, "node_modules/lru-cache": { "version": "6.0.0", @@ -3850,7 +3304,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3858,15 +3312,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minipass": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", @@ -3876,35 +3321,22 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/mlly": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", - "integrity": "sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz", + "integrity": "sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==", "dev": true, "dependencies": { - "acorn": "^8.10.0", - "pathe": "^1.1.1", + "acorn": "^8.11.3", + "pathe": "^1.1.2", "pkg-types": "^1.0.3", - "ufo": "^1.3.0" + "ufo": "^1.3.2" } }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/muggle-string": { "version": "0.3.1", @@ -3944,7 +3376,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "peer": true }, "node_modules/natural-compare-lite": { "version": "1.4.0", @@ -3974,18 +3406,6 @@ "node": ">=0.10.0" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4008,7 +3428,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, + "peer": true, "dependencies": { "wrappy": "1" } @@ -4017,7 +3437,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, + "peer": true, "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", @@ -4031,9 +3451,9 @@ } }, "node_modules/otpauth": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.2.1.tgz", - "integrity": "sha512-/MRvcm63pzK20NCsIOe8Btun42/yWNylPbUo/h5dMpSRJpoAJstWodEUjm4zUDeT1+Vbqif2E8IcP4trl1U4gQ==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.2.2.tgz", + "integrity": "sha512-2VcnYRUmq1dNckIfySNYP32ITWp1bvTeAEW0BSCR6G3GBf3a5zb9E+ubY62t3Dma9RjoHlvd7QpmzHfJZRkiNg==", "dependencies": { "jssha": "~3.3.1" }, @@ -4045,7 +3465,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -4060,7 +3480,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, + "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -4071,20 +3491,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, + "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -4102,6 +3513,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "peer": true, "engines": { "node": ">=8" } @@ -4110,7 +3522,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4119,7 +3531,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -4147,18 +3558,18 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" } }, "node_modules/pathe": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", - "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", "dev": true }, "node_modules/periscopic": { @@ -4274,15 +3685,6 @@ "pathe": "^1.1.0" } }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "peer": true, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/postcss": { "version": "8.4.33", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", @@ -4432,48 +3834,16 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, + "peer": true, "engines": { "node": ">= 0.8.0" } }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "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/pug-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", - "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==", - "dev": true - }, - "node_modules/pug-lexer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", - "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", - "dev": true, - "dependencies": { - "character-parser": "^2.2.0", - "is-expression": "^4.0.0", - "pug-error": "^2.0.0" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4482,29 +3852,18 @@ "node": ">=6" } }, - "node_modules/qrcode": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz", - "integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==", - "peer": true, - "dependencies": { - "dijkstrajs": "^1.0.1", - "encode-utf8": "^1.0.3", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" + "node_modules/qrcode.vue": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.4.1.tgz", + "integrity": "sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==", + "peerDependencies": { + "vue": "^3.0.0" } }, "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", @@ -4540,15 +3899,6 @@ "node": ">=8.10.0" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -4557,12 +3907,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "peer": true - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -4584,7 +3928,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, + "peer": true, "engines": { "node": ">=4" } @@ -4593,7 +3937,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -4603,7 +3946,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -4615,9 +3958,9 @@ } }, "node_modules/rollup": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.4.tgz", - "integrity": "sha512-2ztU7pY/lrQyXSCnnoU4ICjT/tCG9cdH3/G25ERqE3Lst6vl2BCM5hL2Nw+sslAvAf+ccKsAq1SkKQALyqhR7g==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz", + "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -4630,19 +3973,19 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.9.4", - "@rollup/rollup-android-arm64": "4.9.4", - "@rollup/rollup-darwin-arm64": "4.9.4", - "@rollup/rollup-darwin-x64": "4.9.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.4", - "@rollup/rollup-linux-arm64-gnu": "4.9.4", - "@rollup/rollup-linux-arm64-musl": "4.9.4", - "@rollup/rollup-linux-riscv64-gnu": "4.9.4", - "@rollup/rollup-linux-x64-gnu": "4.9.4", - "@rollup/rollup-linux-x64-musl": "4.9.4", - "@rollup/rollup-win32-arm64-msvc": "4.9.4", - "@rollup/rollup-win32-ia32-msvc": "4.9.4", - "@rollup/rollup-win32-x64-msvc": "4.9.4", + "@rollup/rollup-android-arm-eabi": "4.9.6", + "@rollup/rollup-android-arm64": "4.9.6", + "@rollup/rollup-darwin-arm64": "4.9.6", + "@rollup/rollup-darwin-x64": "4.9.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.6", + "@rollup/rollup-linux-arm64-gnu": "4.9.6", + "@rollup/rollup-linux-arm64-musl": "4.9.6", + "@rollup/rollup-linux-riscv64-gnu": "4.9.6", + "@rollup/rollup-linux-x64-gnu": "4.9.6", + "@rollup/rollup-linux-x64-musl": "4.9.6", + "@rollup/rollup-win32-arm64-msvc": "4.9.6", + "@rollup/rollup-win32-ia32-msvc": "4.9.6", + "@rollup/rollup-win32-x64-msvc": "4.9.6", "fsevents": "~2.3.2" } }, @@ -4650,7 +3993,6 @@ "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", @@ -4670,9 +4012,9 @@ } }, "node_modules/sass": { - "version": "1.69.7", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.7.tgz", - "integrity": "sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", + "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -4686,9 +4028,9 @@ } }, "node_modules/scule": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/scule/-/scule-1.1.1.tgz", - "integrity": "sha512-sHtm/SsIK9BUBI3EFT/Gnp9VoKfY6QLvlkvAE6YK7454IF8FSgJEAnJpVdSC7K5/pjI5NfxhzBLW2JAfYA/shQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.2.0.tgz", + "integrity": "sha512-CRCmi5zHQnSoeCik9565PONMg0kfkvYmcSqrbOJY4txFfy1wvVULV4FDaiXhUblUgahdqz3F2NwHZ8i4eBTwUw==", "dev": true }, "node_modules/semver": { @@ -4706,32 +4048,10 @@ "node": ">=10" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "peer": true - }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -4743,7 +4063,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -4783,23 +4102,21 @@ "node": ">=0.10.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { @@ -4817,6 +4134,39 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -4845,7 +4195,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, + "peer": true, "engines": { "node": ">=8" }, @@ -4939,7 +4289,7 @@ "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, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -4960,16 +4310,17 @@ } }, "node_modules/svelte": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.8.tgz", - "integrity": "sha512-hU6dh1MPl8gh6klQZwK/n73GiAHiR95IkFsesLPbMeEZi36ydaXL/ZAb4g9sayT0MXzpxyZjR28yderJHxcmYA==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.9.tgz", + "integrity": "sha512-hsoB/WZGEPFXeRRLPhPrbRz67PhP6sqYgvwcAs+gWdSQSvNDw+/lTeUJSWe5h2xC97Fz/8QxAOqItwBzNJPU8w==", "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", "acorn": "^8.9.0", "aria-query": "^5.3.0", - "axobject-query": "^3.2.1", + "axobject-query": "^4.0.0", "code-red": "^1.0.3", "css-tree": "^2.3.1", "estree-walker": "^3.0.3", @@ -5031,7 +4382,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "peer": true }, "node_modules/thenify": { "version": "3.3.1", @@ -5088,168 +4439,11 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" - } - }, - "node_modules/tslint/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/tslint/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/tslint/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/tslint/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/tslint/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/tslint/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tslint/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, + "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -5261,7 +4455,7 @@ "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, + "peer": true, "engines": { "node": ">=10" }, @@ -5289,9 +4483,9 @@ "dev": true }, "node_modules/universal-cookie": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.0.1.tgz", - "integrity": "sha512-6OuX9xELF6dsVJeADJAYNDOxQf/NR3Na5bGCRd+hkysMDkSt79jJ4tdv5OBe+ZgAks3ExHBdCXkD2SjqLyK59w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.0.2.tgz", + "integrity": "sha512-EC9PA+1nojhJtVnKW2Z7WYah01jgYJApqhX+Y8XU97TnFd7KaoxWTHiTZFtfpfV50jEF1L8V5p64ZxIx3Q67dg==", "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0" @@ -5448,9 +4642,9 @@ } }, "node_modules/vite": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.11.tgz", - "integrity": "sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", "dev": true, "dependencies": { "esbuild": "^0.19.3", @@ -5502,79 +4696,16 @@ } } }, - "node_modules/vls": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/vls/-/vls-0.8.5.tgz", - "integrity": "sha512-61kbdO2COZWBMC4wq59QfDdev9ruXd0226f57DFJTFpFXv85S+qnHakQlAmbSYFFLGKcx95HB2UjnuQh4YRwFA==", - "dev": true, - "dependencies": { - "eslint": "^8.34.0", - "eslint-plugin-vue": "^9.9.0", - "prettier": "^2.8.4", - "pug-lexer": "^5.0.1", - "tslint": "6.1.3", - "typescript": "^4.9.5" - }, - "bin": { - "vls": "bin/vls" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/vls/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/vscode-html-languageservice": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.1.1.tgz", - "integrity": "sha512-JenrspIIG/Q+93R6G3L6HdK96itSisMynE0glURqHpQbL3dKAKzdm8L40lAHNkwJeBg+BBPpAshZKv/38onrTQ==", - "dev": true, - "dependencies": { - "@vscode/l10n": "^0.0.16", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-languageserver-types": "^3.17.5", - "vscode-uri": "^3.0.8" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", - "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==", - "dev": true - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "dev": true - }, - "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", - "dev": true - }, "node_modules/vue": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.7.tgz", - "integrity": "sha512-4urmkWpudekq0CPNMO7p6mBGa9qmTXwJMO2r6CT4EzIJVG7WoSReiysiNb7OSi/WI113oX0Srn9Rz1k/DCXKFQ==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz", + "integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==", "dependencies": { - "@vue/compiler-dom": "3.4.7", - "@vue/compiler-sfc": "3.4.7", - "@vue/runtime-dom": "3.4.7", - "@vue/server-renderer": "3.4.7", - "@vue/shared": "3.4.7" + "@vue/compiler-dom": "3.4.15", + "@vue/compiler-sfc": "3.4.15", + "@vue/runtime-dom": "3.4.15", + "@vue/server-renderer": "3.4.15", + "@vue/shared": "3.4.15" }, "peerDependencies": { "typescript": "*" @@ -5586,9 +4717,9 @@ } }, "node_modules/vue-eslint-parser": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.0.tgz", - "integrity": "sha512-7KsNBb6gHFA75BtneJsoK/dbZ281whUIwFYdQxA68QrCrGMXYzUMbPDHGcOQ0OocIVKrWSKWXZ4mL7tonCXoUw==", + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz", + "integrity": "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==", "dev": true, "dependencies": { "debug": "^4.3.4", @@ -5686,7 +4817,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -5697,24 +4827,21 @@ "node": ">= 8" } }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "peer": true - }, "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "peer": true, + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrap-ansi-cjs": { @@ -5735,134 +4862,91 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "peer": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "peer": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "peer": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "peer": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "node": ">=12" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/yargs/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "peer": true, - "dependencies": { - "p-locate": "^4.1.0" - }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "peer": true, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { - "p-try": "^2.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=6" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/yargs/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "peer": true, - "dependencies": { - "p-limit": "^2.2.0" - }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "peer": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, "engines": { - "node": ">=8" + "node": ">= 14" } }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, + "peer": true, "engines": { "node": ">=10" }, diff --git a/front-end/package.json b/front-end/package.json index b29b298..75a3d9f 100644 --- a/front-end/package.json +++ b/front-end/package.json @@ -1,7 +1,7 @@ { "name": "@vnuge/cmnext-front-end", "private": true, - "version": "0.1.2", +"version": "0.1.2", "type": "module", "copyright": "Copyright \u00A9 2024 Vaughn Nugent", "description": "The CMNext admin web UI, built with Tailwindcss and Vuejs", @@ -10,7 +10,7 @@ "output_dir": "bin", "main": "index.html", - "license": "agpl3", +"license": "agpl3", "scripts": { "dev": "vite", @@ -20,7 +20,6 @@ }, "dependencies": { - "@chenfengyuan/vue-qrcode": "^2.0.0", "@fontsource/source-sans-pro": "^5.0.8", "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0", @@ -34,6 +33,7 @@ "@vuelidate/validators": "^2.0.2", "@vueuse/core": "^10.3.x", "@vueuse/router": "^10.3.x", + "qrcode.vue": "^3.4.1", "axios": "^1.4.0", "base32-encode": "^2.0.0", "jose": "^5.0.x", @@ -50,7 +50,6 @@ "@types/showdown": "^2.0.1", "@types/lodash-es": "^4.14.194", "@vitejs/plugin-vue": "^5.0.x", - "@volar-plugins/vetur": "latest", "autoprefixer": "^10.4.14", "dotenv": "^16.0.3", "postcss": "^8.4.23", @@ -58,7 +57,7 @@ "tailwindcss": "^3.3.2", "typescript": "^5.0.2", "vite": "^5.0.x", - "unplugin-vue-router": "^0.7.0", + "unplugin-vue-router": "^0.7.0", "vue-eslint-parser": "^9.3.0", "vue-router": "^4.2.0", "vue-tsc": "^1.4.2", diff --git a/front-end/src/bootstrap/Environment.vue b/front-end/src/bootstrap/Environment.vue index 618ee62..c14b7b6 100644 --- a/front-end/src/bootstrap/Environment.vue +++ b/front-end/src/bootstrap/Environment.vue @@ -1,3 +1,42 @@ +<script setup lang="ts"> + +import { computed, defineAsyncComponent } from 'vue' +import { RouteLocation, useRouter } from 'vue-router' +import { filter, map, without, find, includes } from 'lodash-es' +import { storeToRefs } from 'pinia' +import { useEnvSize } from '@vnuge/vnlib.browser' +import { useStore } from '../store' +import siteHeader from './components/Header.vue' +import siteFooter from './components/Footer.vue' +const ConfirmPrompt = defineAsyncComponent(() => import('./components/ConfirmPrompt.vue')); +const CookieWarning = defineAsyncComponent(() => import('./components/CookieWarning.vue')); +const PasswordPrompt = defineAsyncComponent(() => import('./components/PasswordPrompt.vue')); + +const emit = defineEmits(['logout']) +const store = useStore() +const { showCookieWarning, currentRoutes } = storeToRefs(store) +const { getRoutes } = useRouter(); + +//Use the env size to calculate the header and footer heights for us +const { header, footer, content, headerHeight, footerHeight } = useEnvSize(true) + +const routes = computed<RouteLocation[]>(() => { + // Get routes that are defined above but only if they are defined in the router + // This is a computed property because loggedin is a reactive property + + const routes = filter(getRoutes(), (pageName) => includes(currentRoutes.value, pageName.name)) + + const activeRoutes = map(currentRoutes.value, route => find(routes, { name: route })) + + return without<RouteLocation>(activeRoutes, undefined) +}) + +//Forces the page content to be exactly the height of the viewport - header and footer sizes +const bodyStyle = computed(() => ({ 'min-height': `calc(100vh - ${headerHeight.value + footerHeight.value}px)` })) +const generalToastStyle = computed(() => ({ top: `${headerHeight.value + 5}px` })) +const formToastStyle = computed(() => ({ top: `${headerHeight.value}px` })) + +</script> <template> <div id="env-entry" ref="content" class="absolute top-0 left-0 w-full min-h-screen env-bg"> <div class="absolute flex w-full"> @@ -44,43 +83,3 @@ <ConfirmPrompt /> </div> </template> - -<script setup lang="ts"> - -import { computed } from 'vue' -import { RouteLocation, useRouter } from 'vue-router' -import { filter, map, without, find, includes } from 'lodash-es' -import { storeToRefs } from 'pinia' -import { useEnvSize } from '@vnuge/vnlib.browser' -import { useStore } from '../store' -import CookieWarning from './components/CookieWarning.vue' -import PasswordPrompt from './components/PasswordPrompt.vue' -import siteHeader from './components/Header.vue' -import siteFooter from './components/Footer.vue' -import ConfirmPrompt from './components/ConfirmPrompt.vue' - -const emit = defineEmits(['logout']) -const store = useStore() -const { showCookieWarning, currentRoutes } = storeToRefs(store) -const { getRoutes } = useRouter(); - -//Use the env size to calculate the header and footer heights for us -const { header, footer, content, headerHeight, footerHeight } = useEnvSize(true) - -const routes = computed<RouteLocation[]>(() => { - // Get routes that are defined above but only if they are defined in the router - // This is a computed property because loggedin is a reactive property - - const routes = filter(getRoutes(), (pageName) => includes(currentRoutes.value, pageName.name)) - - const activeRoutes = map(currentRoutes.value, route => find(routes, { name: route })) - - return without<RouteLocation>(activeRoutes, undefined) -}) - -//Forces the page content to be exactly the height of the viewport - header and footer sizes -const bodyStyle = computed(() => ({ 'min-height': `calc(100vh - ${headerHeight.value + footerHeight.value}px)` })) -const generalToastStyle = computed(() => ({ top: `${headerHeight.value + 5}px` })) -const formToastStyle = computed(() => ({ top: `${headerHeight.value}px` })) - -</script> diff --git a/front-end/src/bootstrap/components/ConfirmPrompt.vue b/front-end/src/bootstrap/components/ConfirmPrompt.vue index c67bcfc..3994672 100644 --- a/front-end/src/bootstrap/components/ConfirmPrompt.vue +++ b/front-end/src/bootstrap/components/ConfirmPrompt.vue @@ -1,3 +1,37 @@ +<script setup lang="ts"> +import { defaultTo, noop } from 'lodash-es' +import { computed, ref } from 'vue' +import { Dialog, DialogPanel, DialogTitle, DialogDescription } from '@headlessui/vue' +import { onClickOutside } from '@vueuse/core' +import { useConfirm, useEnvSize } from '@vnuge/vnlib.browser' + +export interface ConfirmMessage { + title: string + text: string + subtext?: string +} + +const { headerHeight } = useEnvSize() +//Use component side of confirm +const { isRevealed, confirm, cancel, onReveal } = useConfirm() + +const dialog = ref(null) +const message = ref<ConfirmMessage>() + +//Cancel prompt when user clicks outside of dialog, only when its open +onClickOutside(dialog, () => isRevealed.value ? cancel() : noop()) + +//Set message on reveal +onReveal(m => message.value = defaultTo(m, {})); + +const style = computed(() => { + return { + 'height': `calc(100vh - ${headerHeight.value}px)`, + 'top': `${headerHeight.value}px` + } +}) + +</script> <template> <div id="confirm-prompt"> @@ -5,15 +39,15 @@ <div class="modal-content-container"> <DialogPanel> <DialogTitle class="modal-title"> - {{ message.title ?? 'Confirm' }} + {{ message?.title ?? 'Confirm' }} </DialogTitle> <DialogDescription class="modal-description"> - {{ message.text }} + {{ message?.text }} </DialogDescription> <p class="modal-text-secondary"> - {{ message.subtext }} + {{ message?.subtext }} </p> <div class="modal-button-container"> @@ -29,39 +63,3 @@ </Dialog> </div> </template> - -<script setup lang="ts"> -import { defaultTo } from 'lodash-es' -import { computed, ref } from 'vue' - -import { - Dialog, - DialogPanel, - DialogTitle, - DialogDescription, -} from '@headlessui/vue' - -import { onClickOutside } from '@vueuse/core' -import { useConfirm, useEnvSize } from '@vnuge/vnlib.browser' - -const { headerHeight } = useEnvSize() -//Use component side of confirm -const { isRevealed, confirm, cancel, onReveal } = useConfirm() - -const dialog = ref(null) -const message = ref({}) - -//Cancel prompt when user clicks outside of dialog, only when its open -onClickOutside(dialog, () => isRevealed.value ? cancel() : null) - -//Set message on reveal -onReveal(m => message.value = defaultTo(m, {})); - -const style = computed(() => { - return { - 'height': `calc(100vh - ${headerHeight.value}px)`, - 'top': `${headerHeight.value}px` - } -}) - -</script>
\ No newline at end of file diff --git a/front-end/src/bootstrap/components/CookieWarning.vue b/front-end/src/bootstrap/components/CookieWarning.vue index b5239f5..2651cd1 100644 --- a/front-end/src/bootstrap/components/CookieWarning.vue +++ b/front-end/src/bootstrap/components/CookieWarning.vue @@ -1,15 +1,4 @@ -<template> - <div v-if="show" class="fixed top-0 left-0 z-10 w-full" :style="style"> - <div class="flex w-full p-2 text-center text-white bg-blue-600"> - <div class="m-auto text-sm font-semibold md:text-base"> - You must have cookies enabled for this site to work properly - </div> - </div> - </div> -</template> - <script setup lang="ts"> - import { computed, toRefs } from 'vue' import { useEnvSize } from '@vnuge/vnlib.browser' @@ -18,19 +7,20 @@ const props = defineProps<{ }>() const { hidden } = toRefs(props) - const { headerHeight } = useEnvSize() const show = computed(() => (!window.navigator.cookieEnabled) && !hidden.value) - -const style = computed(() => { - return { - top: headerHeight.value + 'px' - } -}) +const style = computed(() => ({ top: headerHeight.value + 'px' })) </script> +<template> + <div v-if="show" class="fixed top-0 left-0 z-10 w-full" :style="style"> + <div class="flex w-full p-2 text-center text-white bg-blue-600"> + <div class="m-auto text-sm font-semibold md:text-base"> + You must have cookies enabled for this site to work properly + </div> + </div> + </div> +</template> -<style> -</style> diff --git a/front-end/src/bootstrap/components/Footer.vue b/front-end/src/bootstrap/components/Footer.vue index 7c306c9..bdd0c92 100644 --- a/front-end/src/bootstrap/components/Footer.vue +++ b/front-end/src/bootstrap/components/Footer.vue @@ -1,9 +1,19 @@ +<script setup lang="ts"> +import { useDark } from '@vueuse/core' +import { debounce } from 'lodash-es' + +const isDark = useDark() + +const Dark = debounce(() => isDark.value = true, 50) +const Light = debounce(() => isDark.value = false, 50) +</script> + <template> <footer id="vn-footer" class="bottom-0 left-0 z-10 w-full"> <div id="footer-content" class="footer-content" > <div class="footer-main-container"> <div id="footer-text-container" class="col-span-4 sm:col-span-6 lg:col-span-3"> - <p class="my-4 text-sm leading-normal"> + <p class="my-4 text-xs leading-normal"> CMNext ia a AGPL3 licensed free and open source content management system </p> </div> @@ -50,19 +60,9 @@ </p> </div> <div class="mb-6 text-left md:mb-0"> - Copyright © 2023 Vaughn Nugent. All Rights Reserved. + Copyright © 2024 Vaughn Nugent. </div> </div> </div> </footer> </template> - -<script setup lang="ts"> -import { useDark } from '@vueuse/core' -import { debounce } from 'lodash-es' - -const isDark = useDark() - -const Dark = debounce(() => isDark.value = true, 50) -const Light = debounce(() => isDark.value = false, 50) -</script> diff --git a/front-end/src/bootstrap/components/Header.vue b/front-end/src/bootstrap/components/Header.vue index 43a805b..6093fdc 100644 --- a/front-end/src/bootstrap/components/Header.vue +++ b/front-end/src/bootstrap/components/Header.vue @@ -1,78 +1,4 @@ <!-- eslint-disable vue/max-attributes-per-line --> -<template> - <header class="sticky top-0 left-0 z-40"> - <div class="flex header-container"> - <div id="header-mobile-menu" ref="sideMenu" class="side-menu" :style="sideMenuStyle"> - <div class="pt-4 pl-4 pr-6"> - <nav id="header-mobile-nav" class="relative flex flex-col pr-3"> - <div v-for="route in routes" :key="route.path" class="m-auto ml-0"> - <div class="my-1" @click="closeSideMenu"> - <router-link :to="route"> - {{ route.name }} - </router-link> - </div> - </div> - </nav> - </div> - </div> - <div class="flex flex-row w-full md:mx-3"> - <div class="hidden w-4 lg:block" /> - <div class="flex px-4 my-auto text-xl md:hidden"> - <div v-if="!sideMenuActive" class="w-7" @click.prevent="openSideMenu"> - <fa-icon icon="bars" /> - </div> - <div v-else class="text-2xl w-7"> - <fa-icon icon="times" /> - </div> - </div> - <div id="site-title-container" class="flex m-0 mr-3"> - <div class="inline-block px-1"> - <slot name="site_logo" /> - </div> - <div id="site-title" class="inline-block m-auto mx-1"> - <router-link to="/"> - <h3>{{ siteTitle }}</h3> - </router-link> - </div> - </div> - <div class="hidden w-4 lg:block" /> - <nav id="header-desktop-nav" class="flex-row hidden mr-2 md:flex"> - <span v-for="route in routes" :key="route.fullPath" class="flex px-1 lg:px-3"> - <div v-if="!route.hide" class="m-auto"> - <router-link :to="route" class="flex-auto"> - {{ route.name }} - </router-link> - </div> - </span> - </nav> - <div id="user-menu" ref="userMenu" class="drop-controller" :class="{ 'hovered': userMenuHovered }"> - <div class="user-menu"> - Hello <span class="font-semibold">{{ uname }}</span> - </div> - <div ref="userDrop" class="absolute top-0 right-0 duration-100 ease-in-out" style="z-index:-1" :style="dropStyle"> - <div class="drop-menu" @click.prevent="userMenuHovered = false"> - <span class="space-x-2" /> - <a v-if="!loggedIn" href="#" data-header-dropdown="register" @click="gotoRoute('/register')"> - Register - </a> - <a v-else href="#" data-header-dropdown="account" @click="gotoRoute('/account')"> - Account - </a> - <a v-if="!loggedIn" href="#" data-header-dropdown="login" @click="gotoRoute('/login')"> - Login - </a> - <a v-else href="#" data-header-dropdown="logout" @click.prevent="OnLogout"> - Logout - </a> - </div> - </div> - </div> - <div class="hidden space-x-4 lg:block" /> - </div> - </div> - </header> -</template> - <script setup lang="ts"> import { debounce, find } from 'lodash-es' @@ -80,7 +6,7 @@ import { useElementSize, onClickOutside, useElementHover } from '@vueuse/core' import { computed, ref, toRefs } from 'vue' import { useEnvSize } from '@vnuge/vnlib.browser' import { RouteLocation, useRouter } from 'vue-router'; -import { storeToRefs } from 'pinia'; +import { storeToRefs } from 'pinia'; import { useStore } from '../../store'; const emit = defineEmits(['logout']) @@ -92,7 +18,6 @@ const { routes } = toRefs(props) const store = useStore(); const { loggedIn, siteTitle } = storeToRefs(store); - const { headerHeight } = useEnvSize() //Get the router for navigation @@ -132,7 +57,7 @@ const openSideMenu = debounce(() => sideMenuActive.value = true, 50) onClickOutside(sideMenu, closeSideMenu) //Redirect to the route when clicking on it -const gotoRoute = (route : string) =>{ +const gotoRoute = (route: string) => { //Get all routes from the router const allRoutes = router.getRoutes(); @@ -140,19 +65,90 @@ const gotoRoute = (route : string) =>{ //Try to find the route by its path const goto = find(allRoutes, { path: route }); - if(goto){ + if (goto) { //navigate to the route manually router.push(goto); } - else{ + else { //Fallback to full navigation window.location.assign(route); } } -const OnLogout = () =>{ - //Emit logout event - emit('logout') -} +//Emit logout event +const OnLogout = () => emit('logout') -</script>
\ No newline at end of file +</script> +<template> + <header class="sticky top-0 left-0 z-40"> + <div class="flex header-container"> + <div id="header-mobile-menu" ref="sideMenu" class="side-menu" :style="sideMenuStyle"> + <div class="pt-4 pl-4 pr-6"> + <nav id="header-mobile-nav" class="relative flex flex-col pr-3"> + <div v-for="route in routes" :key="route.path" class="m-auto ml-0"> + <div class="my-1" @click="closeSideMenu"> + <router-link :to="route"> + {{ route.name }} + </router-link> + </div> + </div> + </nav> + </div> + </div> + <div class="flex flex-row w-full md:mx-3"> + <div class="hidden w-4 lg:block" /> + <div class="flex px-4 my-auto text-xl md:hidden"> + <div v-if="!sideMenuActive" class="w-7" @click.prevent="openSideMenu"> + <fa-icon icon="bars" /> + </div> + <div v-else class="text-2xl w-7"> + <fa-icon icon="times" /> + </div> + </div> + <div id="site-title-container" class="flex m-0 mr-3"> + <div class="inline-block px-1"> + <slot name="site_logo" /> + </div> + <div id="site-title" class="inline-block m-auto mx-1"> + <router-link to="/"> + <h3>{{ siteTitle }}</h3> + </router-link> + </div> + </div> + <div class="hidden w-4 lg:block" /> + <nav id="header-desktop-nav" class="flex-row hidden mr-2 md:flex"> + <span v-for="route in routes" :key="route.fullPath" class="flex px-1 lg:px-3"> + <div v-if="!route.hide" class="m-auto"> + <router-link :to="route" class="flex-auto"> + {{ route.name }} + </router-link> + </div> + </span> + </nav> + <div id="user-menu" ref="userMenu" class="drop-controller" :class="{ 'hovered': userMenuHovered }"> + <div class="user-menu"> + Hello <span class="font-semibold">{{ uname }}</span> + </div> + <div ref="userDrop" class="absolute top-0 right-0 duration-100 ease-in-out" style="z-index:-1" :style="dropStyle"> + <div class="drop-menu" @click.prevent="userMenuHovered = false"> + <span class="space-x-2" /> + <a v-if="!loggedIn" href="#" data-header-dropdown="register" @click="gotoRoute('/register')"> + Register + </a> + <a v-else href="#" data-header-dropdown="account" @click="gotoRoute('/account')"> + Account + </a> + <a v-if="!loggedIn" href="#" data-header-dropdown="login" @click="gotoRoute('/login')"> + Login + </a> + <a v-else href="#" data-header-dropdown="logout" @click.prevent="OnLogout"> + Logout + </a> + </div> + </div> + </div> + <div class="hidden space-x-4 lg:block" /> + </div> + </div> + </header> +</template> diff --git a/front-end/src/main.ts b/front-end/src/main.ts index 4d62df4..3cc3bfa 100644 --- a/front-end/src/main.ts +++ b/front-end/src/main.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Vaughn Nugent +// Copyright (C) 2024 Vaughn Nugent // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as @@ -35,7 +35,7 @@ import { faGithub, faDiscord, faMarkdown } from '@fortawesome/free-brands-svg-ic library.add(faSignInAlt, faGithub, faDiscord, faSpinner, faCertificate, faKey, faSync, faPlus, faMinusCircle, faUser, faCheck, faTrash, faCopy, faPencil, faLink, faPhotoFilm, faRotateLeft, faMarkdown, faBullhorn, faFolderOpen, faComment, faChevronLeft, faChevronRight, faFileDownload, faCode, faFile, faVideo, faImage, faHeadphones, faFileZipper - ); +); //Add icons to library import router from './router' @@ -47,6 +47,7 @@ import SiteLogo from './components/Site-Logo.vue' import DynamicFormVue from './components/DynamicForm.vue' import { globalStatePlugin } from './store/globalState' +import { oauth2AppsPlugin } from './store/oauthAppsPlugin' import { profilePlugin } from './store/userProfile' import { mfaSettingsPlugin } from './store/mfaSettingsPlugin' import { pageProtectionPlugin } from './store/pageProtectionPlugin' @@ -90,16 +91,18 @@ createVnApp({ app.use(router) store.use(globalStatePlugin) + //Add page protection plugin + .use(pageProtectionPlugin(router)) //User-profile plugin .use(profilePlugin('/account/profile')) - //setup page protection plugin with the router - .use(pageProtectionPlugin(router)) //Enable mfa with totp settings plugin (optional pki config) .use(mfaSettingsPlugin('/account/mfa', '/account/pki')) - //Setup social mfa plugin - .use(socialMfaPlugin()) + //Setup social oauth + .use(socialMfaPlugin("/login/social/portals")) //Setup blog state .use(cmnextAdminPlugin(router, 'https://cdn.ckeditor.com/ckeditor5/40.0.0/super-build/ckeditor.js', 15)) + //Use the oauth2 plugin store (disabled for now) + //.use(oauth2AppsPlugin('/oauth/apps', '/oauth/scopes')) //Add the home-page component router.addRoute({ diff --git a/front-end/src/store/index.ts b/front-end/src/store/index.ts index 1b2d7ee..936dddf 100644 --- a/front-end/src/store/index.ts +++ b/front-end/src/store/index.ts @@ -16,10 +16,12 @@ import { useSession } from "@vnuge/vnlib.browser"; import { set } from "@vueuse/core"; import { defineStore } from "pinia"; -import { computed, shallowRef } from "vue"; +import { computed, shallowRef, type UnwrapNestedRefs } from "vue"; export { SortType, QueryType } from './sharedTypes' +export const storeExport = <T>(val: T): UnwrapNestedRefs<T> => val as UnwrapNestedRefs<T>; + /** * Loads the main store for the application */ diff --git a/front-end/src/store/mfaSettingsPlugin.ts b/front-end/src/store/mfaSettingsPlugin.ts index dffafce..b801f32 100644 --- a/front-end/src/store/mfaSettingsPlugin.ts +++ b/front-end/src/store/mfaSettingsPlugin.ts @@ -1,62 +1,99 @@ import 'pinia' -import { MaybeRef, shallowRef, watch } from 'vue'; -import { MfaMethod, PkiPublicKey, apiCall, useMfaConfig, usePkiConfig, usePkiAuth } from '@vnuge/vnlib.browser'; -import { useToggle, get } from '@vueuse/core'; +import { MaybeRef, ref, shallowRef, watch } from 'vue'; +import { MfaMethod, PkiPublicKey, apiCall, useMfaConfig, usePkiConfig, usePkiAuth, MfaApi } from '@vnuge/vnlib.browser'; +import { useToggle, get, set } from '@vueuse/core'; import { PiniaPluginContext, PiniaPlugin, storeToRefs } from 'pinia' import { includes } from 'lodash-es'; +import { storeExport, } from './index'; + +interface PkiStore { + publicKeys: PkiPublicKey[] + pkiConfig: ReturnType<typeof usePkiConfig> + pkiAuth: ReturnType<typeof usePkiAuth> + refresh: () => void +} + +export interface MfaSettingsStore{ + mfa:{ + enabledMethods: MfaMethod[] + refresh: () => void + } & MfaApi + pki?: PkiStore +} declare module 'pinia' { - export interface PiniaCustomProperties { - mfaEndabledMethods: MfaMethod[] - mfaConfig: ReturnType<typeof useMfaConfig> - pkiConfig: ReturnType<typeof usePkiConfig> - pkiAuth: ReturnType<typeof usePkiAuth> - pkiPublicKeys: PkiPublicKey[] - mfaRefreshMethods: () => void + export interface PiniaCustomProperties extends MfaSettingsStore { + } } export const mfaSettingsPlugin = (mfaEndpoint: MaybeRef<string>, pkiEndpoint?:MaybeRef<string>): PiniaPlugin => { - return ({ store }: PiniaPluginContext) => { + return ({ store }: PiniaPluginContext): MfaSettingsStore => { const { loggedIn } = storeToRefs(store) const mfaConfig = useMfaConfig(mfaEndpoint) - const pkiConfig = usePkiConfig(pkiEndpoint || '/') - const pkiAuth = usePkiAuth(pkiEndpoint || '/') - const [onRefresh, mfaRefreshMethods] = useToggle() + + const [onRefresh, refresh] = useToggle() + + const enabledMethods = ref<MfaMethod[]>([]) + + const usePki = () => { + + const publicKeys = shallowRef<PkiPublicKey[]>([]) + + const pkiConfig = usePkiConfig(pkiEndpoint || '/') + const pkiAuth = usePkiAuth(pkiEndpoint || '/') + + //Watch for changes to mfa methods (refresh) and update the pki keys + watch([enabledMethods], ([methods]) => { + if (!includes(methods, 'pki' as MfaMethod) || !get(pkiEndpoint)) { + set(publicKeys, []) + return + } - const mfaEndabledMethods = shallowRef<MfaMethod[]>([]) - const pkiPublicKeys = shallowRef<PkiPublicKey[]>([]) + //load the pki keys if pki is enabled + apiCall(async () => publicKeys.value = await pkiConfig.getAllKeys()) + }) + + return{ + publicKeys, + pkiConfig, + pkiAuth, + refresh + } + } watch([loggedIn, onRefresh], ([ li ]) => { if(!li){ - mfaEndabledMethods.value = [] + set(enabledMethods, []) return } //load the mfa methods if the user is logged in - apiCall(async () => mfaEndabledMethods.value = await mfaConfig.getMethods()) - }) - - //Watch for changes to mfa methods (refresh) and update the pki keys - watch([mfaEndabledMethods], ([ methods ]) => { - if(!includes(methods, 'pki' as MfaMethod) || !get(pkiEndpoint)){ - pkiPublicKeys.value = [] - return - } - - //load the pki keys if pki is enabled - apiCall(async () => pkiPublicKeys.value = await pkiConfig.getAllKeys()) + apiCall(async () => enabledMethods.value = await mfaConfig.getMethods()) }) - return{ - mfaRefreshMethods, - mfaEndabledMethods, - mfaConfig, - pkiConfig, - pkiAuth, - pkiPublicKeys + //Only return the pki store if pki is enabled + if(get(pkiEndpoint)){ + return storeExport({ + mfa:{ + enabledMethods, + refresh, + ...mfaConfig + }, + pki: usePki() + }) + } + else{ + return storeExport({ + mfa:{ + enabledMethods, + refresh, + ...mfaConfig + }, + }) + } } }
\ No newline at end of file diff --git a/front-end/src/store/oauthAppsPlugin.ts b/front-end/src/store/oauthAppsPlugin.ts new file mode 100644 index 0000000..7a76992 --- /dev/null +++ b/front-end/src/store/oauthAppsPlugin.ts @@ -0,0 +1,154 @@ +import 'pinia' +import { MaybeRef, computed, ref, shallowRef, watch } from 'vue'; +import { apiCall, useAxios } from '@vnuge/vnlib.browser'; +import { get, set, useToggle } from '@vueuse/core'; +import { PiniaPlugin, PiniaPluginContext, storeToRefs } from 'pinia' +import { map, sortBy, isArray } from 'lodash-es'; +import { storeExport } from '.'; + +export interface OAuth2Application { + readonly Id: string, + readonly name: string, + readonly description: string, + readonly permissions: string[], + readonly client_id: string, + Created: Date, + readonly LastModified: Date, +} + +export interface NewAppResponse { + readonly secret: string + readonly app: OAuth2Application +} + +export interface Oauth2Store{ + oauth2: { + apps: OAuth2Application[], + scopes: string[], + getApps(): Promise<OAuth2Application[]> + createApp(app: OAuth2Application): Promise<NewAppResponse> + updateAppSecret(app: OAuth2Application, password: string): Promise<string> + updateAppMeta(app: OAuth2Application): Promise<void> + deleteApp(app: OAuth2Application, password: string): Promise<void> + refresh(): void + } +} + +declare module 'pinia' { + export interface PiniaCustomProperties extends Oauth2Store{ + + } +} + +export const oauth2AppsPlugin = (o2EndpointUrl: MaybeRef<string>, scopeEndpoint: MaybeRef<string>): PiniaPlugin =>{ + + return ({ store }: PiniaPluginContext): Oauth2Store => { + + const axios = useAxios(null); + const { loggedIn } = storeToRefs(store) + + const [onRefresh, refresh] = useToggle() + + const _oauth2Apps = shallowRef<OAuth2Application[]>([]) + const scopes = ref<string[]>([]) + + /** + * Updates an Oauth2 application's metadata + */ + const updateAppMeta = async (app: OAuth2Application): Promise<void> => { + //Update the app metadata + await axios.put(get(o2EndpointUrl), app) + } + + /** + * Gets all of the user's oauth2 applications from the server + * @returns The user's oauth2 applications + */ + const getApps = async () => { + // Get all apps + const { data } = await axios.get<OAuth2Application[]>(get(o2EndpointUrl)); + + if(!isArray(data)){ + throw new Error("Invalid response from server") + } + + return map(data, (appData) => { + //Store the created time as a date object + appData.Created = new Date(appData?.Created ?? 0) + //create a new state manager for the user's profile + return appData; + }) + } + + /** + * Creates a new application from the given data + * @param param0 The application server buffer + * @returns The newly created application + */ + const createApp = async ({ name, description, permissions }: OAuth2Application): Promise<NewAppResponse> => { + + // make the post request, response is the new app data with a secret + const { data } = await axios.post<OAuth2Application & { raw_secret: string }>(`${get(o2EndpointUrl)}?action=create`, { name, description, permissions }) + + // Store secret + const secret = data.raw_secret + + // remove secre tfrom the response + delete (data as any).raw_secret + + return { secret, app: data } + } + + /** + * Requets a new secret for an application from the server + * @param app The app to request a new secret for + * @param password The user's password + * @returns The new secret + */ + const updateAppSecret = async (app: OAuth2Application, password: string): Promise<string> => { + const { data } = await axios.post(`${o2EndpointUrl}?action=secret`, { Id: app.Id, password }) + return data.raw_secret + } + + /** + * Deletes an application from the server + * @param app The application to delete + * @param password The user's password + * @returns The response from the server + */ + const deleteApp = async ({ Id }: OAuth2Application, password: string): Promise<void> => { + await axios.post(`${o2EndpointUrl}?action=delete`, { password, Id }); + } + + const apps = computed(() => sortBy(_oauth2Apps.value, a => a.Created)) + + watch([loggedIn, onRefresh], async ([li]) => { + if (!li){ + set(_oauth2Apps, []) + return; + } + + //Load the user's oauth2 apps + apiCall(async () => { + _oauth2Apps.value = await getApps() + + //Load the oauth2 scopes + const { data } = await axios.get<string[]>(get(scopeEndpoint)) + set(scopes, data) + }) + }) + + return storeExport({ + oauth2:{ + apps, + scopes, + getApps, + createApp, + updateAppMeta, + updateAppSecret, + deleteApp, + refresh + } + }) + } +}
\ No newline at end of file diff --git a/front-end/src/store/pageProtectionPlugin.ts b/front-end/src/store/pageProtectionPlugin.ts index 9831dad..a747e49 100644 --- a/front-end/src/store/pageProtectionPlugin.ts +++ b/front-end/src/store/pageProtectionPlugin.ts @@ -60,14 +60,12 @@ export const pageProtectionPlugin = (router: ReturnType<typeof useRouter>): Pini return true; }) - router.afterEach(() => { - //scroll window back to top - window.scrollTo(0, 0) - }) + //scroll window back to top + router.afterEach(() => window.scrollTo(0, 0)) - watch(loggedIn, (loggedIn) => { + watch(loggedIn, (li) => { //If the user gets logged out, redirect to login - if(loggedIn === false && router.currentRoute.value.name !== 'Login'){ + if(li === false && router.currentRoute.value.name !== 'Login'){ router.push({ name: 'Login' }) } }) diff --git a/front-end/src/store/socialMfaPlugin.ts b/front-end/src/store/socialMfaPlugin.ts index b9bce27..3968cf1 100644 --- a/front-end/src/store/socialMfaPlugin.ts +++ b/front-end/src/store/socialMfaPlugin.ts @@ -1,3 +1,4 @@ + import 'pinia' import { MaybeRef } from 'vue'; import { useSocialOauthLogin, useUser, SocialOAuthPortal, fromPortals, useAxios } from '@vnuge/vnlib.browser' @@ -34,30 +35,42 @@ export const socialMfaPlugin = (portalEndpoint?: MaybeRef<string>): PiniaPlugin } } - const _loadPromise = new Promise<SocialMfaPlugin>((resolve, reject) => { + const _loadPromise = new Promise<SocialMfaPlugin>((resolve, _) => { - if(get(portalEndpoint) == null) { + if (get(portalEndpoint) == null) { const socialOauth = useSocialOauthLogin([]) setLogoutMethod(socialOauth) return resolve(socialOauth) } + /* + Try to load social methods from server, if it fails, then we will + fall back to default + */ + defer(async () => { + + let portals: SocialOAuthPortal[] = [] + try { //Get axios instance const axios = useAxios(null) //Get all enabled portals - const { data } = await axios.get<SocialOAuthPortal[]>(get(portalEndpoint)); - //Setup social providers from server portals - const socialOauth = useSocialOauthLogin(fromPortals(data)); - setLogoutMethod(socialOauth); - - resolve(socialOauth) + const { data, headers } = await axios.get<SocialOAuthPortal[]>(get(portalEndpoint)!); + + if(headers['content-type'] === 'application/json') { + portals = data + } } catch (error) { - reject(error) + //Let failure fall back to default } + + //Create social login from available portals + const socialOauth = useSocialOauthLogin(fromPortals(portals)); + setLogoutMethod(socialOauth); + resolve(socialOauth) }) }) diff --git a/front-end/src/store/userProfile.ts b/front-end/src/store/userProfile.ts index a4ea469..0320ace 100644 --- a/front-end/src/store/userProfile.ts +++ b/front-end/src/store/userProfile.ts @@ -3,7 +3,8 @@ import { MaybeRef, watch } from 'vue'; import { ServerDataBuffer, ServerObjectBuffer, UserProfile, WebMessage, apiCall, useAxios, useDataBuffer, useUser } from '@vnuge/vnlib.browser'; import { get, useToggle } from '@vueuse/core'; import { PiniaPlugin, PiniaPluginContext, storeToRefs } from 'pinia' -import { defer } from 'lodash-es'; +import { defer, noop } from 'lodash-es'; +import { storeExport } from './index'; export interface OAuth2Application { readonly Id: string, @@ -24,17 +25,21 @@ interface ExUserProfile extends UserProfile { created: string | Date } +export interface UserProfileStore{ + userProfile: ServerDataBuffer<ExUserProfile, WebMessage<string>> + userName: string | undefined + refreshProfile(): void; +} + declare module 'pinia' { - export interface PiniaCustomProperties { - userProfile: ServerDataBuffer<ExUserProfile, WebMessage<string>> - userName: string | undefined - refreshProfile(): void; + export interface PiniaCustomProperties extends UserProfileStore { + } } export const profilePlugin = (accountsUrl:MaybeRef<string>) :PiniaPlugin => { - return ({ store }: PiniaPluginContext) => { + return ({ store }: PiniaPluginContext): UserProfileStore => { const { loggedIn } = storeToRefs(store) const { getProfile, userName } = useUser() @@ -64,19 +69,16 @@ export const profilePlugin = (accountsUrl:MaybeRef<string>) :PiniaPlugin => { userProfile.apply(profile) } - watch([loggedIn, onRefresh], ([li]) => { - //If the user is logged in, load the profile buffer - if (li) { - apiCall(loadProfile) - } - }) + //If the user is logged in, load the profile buffer + watch([loggedIn, onRefresh], ([li]) => li ? apiCall(loadProfile) : noop()) + //Defer intiial profile load defer(refreshProfile); - return { + return storeExport({ userProfile, refreshProfile, userName - } + }) } }
\ No newline at end of file diff --git a/front-end/src/views/Account/[comp].vue b/front-end/src/views/Account/[comp].vue index d4f1c4d..713a6fe 100644 --- a/front-end/src/views/Account/[comp].vue +++ b/front-end/src/views/Account/[comp].vue @@ -1,3 +1,51 @@ +<script setup lang="ts"> +import { computed } from 'vue' +import { get, set } from '@vueuse/core' +import { useRouteParams } from '@vueuse/router' +import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue' +import { useStore } from '../../store' +import Settings from './components/settings/Settings.vue' +import Profile from './components/profile/Profile.vue' +import OauthApps from './components/oauth/Oauth.vue' + +const store = useStore() +store.setPageTitle('Account') + +const oauthEnabled = computed(() => store.oauth2?.apps); + +type ComponentType = 'profile' | 'oauth' | 'settings' | '' + +const comp = useRouteParams<ComponentType>('comp', '') + +const tabId = computed<number>(() => { + switch (comp.value) { + case 'oauth': + //If oauth is not enabled, redirect to profile + return get(oauthEnabled) ? 2 : 0 + case 'settings': + return 1 + case 'profile': + default: + return 0 + } +}) + +const onTabChange = (tabid: number) => { + switch (tabid) { + case 1: + set(comp, 'settings') + break + case 2: + set(comp, 'oauth') + break + case 0: + default: + set(comp, 'profile') + break + } +} + +</script> <template> <div id="account-template" class="app-component-entry"> <TabGroup :selectedIndex="tabId" @change="onTabChange" as="div" class="container h-full m-auto mt-0 mb-10 duration-150 ease-linear text-color-foreground"> @@ -18,6 +66,12 @@ </span> </tab> + <Tab v-if="oauthEnabled" v-slot="{ selected }" > + <span class="page-link" :class="{ 'active': selected }"> + OAuth + </span> + </tab> + </TabList> </div> @@ -31,54 +85,16 @@ <Settings /> </TabPanel> + <TabPanel v-if="oauthEnabled" :unmount="false"> + <OauthApps /> + </TabPanel> + </TabPanels> </TabGroup> </div> </template> -<script setup lang="ts"> -import { computed } from 'vue' -import { useRouteParams } from '@vueuse/router' -import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue' -import { useStore } from '../../store' -import Settings from './components/settings/Settings.vue' -import Profile from './components/profile/Profile.vue' - -const { setPageTitle } = useStore() -setPageTitle('Account') - -enum ComponentType{ - Profile = 'profile', - Settings = 'settings' -} - -const comp = useRouteParams<ComponentType>('comp', '') - -const tabId = computed<number>(() => { - switch (comp.value) { - case ComponentType.Settings: - return 1 - case ComponentType.Profile: - default: - return 0 - } -}) - -const onTabChange = (tabid : number) =>{ - switch (tabid) { - case 1: - comp.value = ComponentType.Settings - break - case 0: - default: - comp.value = ComponentType.Profile - break - } -} - -</script> - <style lang="scss"> #account-template{ diff --git a/front-end/src/views/Account/components/oauth/CreateApp.vue b/front-end/src/views/Account/components/oauth/CreateApp.vue new file mode 100644 index 0000000..038c43f --- /dev/null +++ b/front-end/src/views/Account/components/oauth/CreateApp.vue @@ -0,0 +1,182 @@ +<script setup lang="ts"> +import { indexOf, pull } from 'lodash-es' +import { Ref, ref, toRefs } from 'vue'; +import { Dialog, DialogPanel, DialogTitle } from '@headlessui/vue' +import { set, useClipboard } from '@vueuse/core' +import { apiCall } from '@vnuge/vnlib.browser' +import { getAppValidator } from './o2AppValidation' +import { useStore } from '../../../../store'; +import { OAuth2Application } from '../../../../store/oauthAppsPlugin'; + +const emit = defineEmits(['close']) +const props = defineProps<{ + isOpen: boolean +}>() + +const { isOpen } = toRefs(props); + +//Init the oauth2 app api +const store = useStore() + +const { copied, copy } = useClipboard(); + +const newAppBuffer = ref<Partial<OAuth2Application& { secret: string }>>(); +const newAppPermissions = ref<string[]>([]); + +const { v$, validate, reset } = getAppValidator(newAppBuffer as Ref<OAuth2Application>); + +const close = () => { + set(newAppBuffer, {}); + reset() + emit('close') +} + +const onFormSubmit = async () => { + + // Validate the new app form + if (!await validate()) { + return + } + + // Create the new app + await apiCall(async () => { + + const { secret } = await store.oauth2.createApp(newAppBuffer.value as OAuth2Application) + + // Reset the new app buffer and pass the secret value + set(newAppBuffer, { secret }) + }) + + // reset the validator + v$.value.$reset() +} + +const permissionChanged = (e: any) => { + if (e.target.checked) { + // Make sure the permission is not already in the list + if (indexOf(newAppPermissions.value, e.target.name) > -1) { + return + } + // Add the permission to the list + newAppPermissions.value.push(e.target.name as string) + } else { + // Remove the permission from the list + pull(newAppPermissions.value, e.target.name) + } + // Update the permissions model + v$.value.permissions.$model = newAppPermissions.value.join(',') +} + +</script> +<template> + <Dialog :open="isOpen" @close="close" class="relative z-10"> + <div class="fixed inset-0 bg-black/30" aria-hidden="true" /> + <div class="fixed inset-0 flex justify-center top-20"> + <DialogPanel class="new-o2-app-dialog"> + <DialogTitle>Create app</DialogTitle> + <div class="flex"> + <div class="m-auto mb-3 text-sm"> + <p class="my-1"> + Step 1: Enter a name for your app. + </p> + <p class="my-1"> + Step 2: Submit the form. + </p> + <p class="my-1 text-red-500"> + Step 3: Save your Client ID and Secret somewhere safe. + </p> + </div> + </div> + <!-- If secret is set, show the scret window --> + <div v-if="newAppBuffer?.secret" class="mt-2"> + <div class="block mx-1 sm:inline"> + Secret: + </div> + <div class="px-1 py-4 my-2 break-all border-2 border-gray-300 rounded-lg"> + <div class="text-center secret"> + <span class="block mx-1 sm:inline"> + {{ newAppBuffer.secret }} + </span> + </div> + </div> + <div class="text-sm"> + <p class="p-2"> + This secret will only be displayed <strong>once</strong>, and you cannot request it again. + If you lose it, you will need to update the secret from the app edit pane. + </p> + <p class="p-2"> + Please remember to keep this secret somewhere safe. If an attacker gains + access to it, they will be able to access any APIs on your behalf! + </p> + </div> + <div class="flex justify-end"> + <button v-if="!copied" class="btn primary" @click="copy(newAppBuffer.secret)"> + Copy + </button> + <button v-else class="btn primary" @click="close"> + Done + </button> + </div> + </div> + <div v-else> + <form id="o2-app-creation" class="" @submit.prevent="onFormSubmit"> + <fieldset class="flex flex-col gap-4"> + <div class="input-container"> + <label>App Name</label> + <input + class="w-full mt-1 input primary" + :class="{'invalid':v$.name.$invalid, 'dirty': v$.name.$dirty}" + name="name" + type="text" + v-model="v$.name.$model" + /> + </div> + <div class="input-container"> + <label>Description</label> + <textarea + class="w-full mt-1 input primary" + :class="{ 'invalid': v$.description.$invalid, 'dirty': v$.name.$dirty }" + name="description" + v-model="v$.description.$model" + rows="3" + /> + </div> + <div class="input-container"> + <label>Permissions</label> + <ul class="text-sm"> + <li v-for="scope in store.oauth2.scopes" :key="scope" class="my-1.5"> + <label class="flex cursor-pointer"> + <input class="w-3.5 cursor-pointer" type="checkbox" :name="`02scope-${scope}`" @change="permissionChanged"> + <span class="my-auto ml-1.5">{{ scope }}</span> + </label> + </li> + </ul> + </div> + </fieldset> + <div class="flex justify-end mt-4"> + <div class="button-group"> + <button type="submit" form="o2-app-creation" class="btn primary">Submit</button> + <button class="btn" @click.prevent="close">Cancel</button> + </div> + </div> + </form> + </div> + </DialogPanel> + </div> + </Dialog> +</template> +<style lang="scss"> + +.new-o2-app-dialog{ + @apply w-full max-w-lg p-8 pt-4 m-auto mt-0 shadow-md sm:rounded border dark:border-dark-500; + @apply bg-white dark:bg-dark-700 dark:text-gray-200; + + #o2-app-creation{ + input.dirty.invalid, + textarea.dirty.invalid{ + @apply border-red-500 focus:border-red-500; + } + } +} + +</style>
\ No newline at end of file diff --git a/front-end/src/views/Account/components/oauth/Oauth.vue b/front-end/src/views/Account/components/oauth/Oauth.vue new file mode 100644 index 0000000..d269689 --- /dev/null +++ b/front-end/src/views/Account/components/oauth/Oauth.vue @@ -0,0 +1,78 @@ +<script setup lang="ts"> +import { defineAsyncComponent } from 'vue' +import { storeToRefs } from 'pinia' +import { useStore } from '../../../../store' +const CreateApp = defineAsyncComponent(() => import('./CreateApp.vue')) + +import SingleApplication from './SingleApplication.vue' +import { useToggle } from '@vueuse/core'; + +const store = useStore() +const { isLocalAccount } = storeToRefs(store) + +const [editNew, toggleEdit] = useToggle() + +const newAppClose = () => { + toggleEdit(false); + //Reload apps on close + store.oauth2.refresh(); +} + +//Load apps +store.oauth2.refresh(); + +</script> +<template> + <div id="oauth-apps" class="acnt-content-container"> + <div class="app-container panel-container"> + <div class="mb-6 panel-header"> + <div class="flex ml-0 mr-auto"> + <div class="my-auto panel-title"> + <h4>Your applications</h4> + </div> + </div> + <div class="ml-auto mr-0"> + <div class="button-container"> + <button class="btn primary sm" :disabled="!isLocalAccount" @click="toggleEdit(true)"> + Create App + </button> + </div> + </div> + </div> + <div v-if="store.oauth2.apps.length == 0" class="no-apps-container"> + <div class="m-auto"> + You dont have any OAuth2 client applications yet. + </div> + </div> + <div v-else> + <div v-for="app in store.oauth2.apps" :key="app.Id" class="panel-content"> + <SingleApplication :application="app" :allow-edit="isLocalAccount" /> + </div> + </div> + </div> + <div class="px-2 my-10"> + <div class="m-auto text-sm"> + OAuth2 applications allow you grant api access to OAuth2 clients using the Client Credentials grant type. + <a class="link" href="https://oauth.net" target="_blank"> + Learn more + </a> + </div> + <div v-show="!isLocalAccount" class="mt-3 text-center text-red-500"> + You may not create or edit applications if you are using external authentication. + </div> + </div> + <CreateApp :is-open="editNew" @close="newAppClose" /> + </div> +</template> + +<style> + +#oauth-apps { + @apply m-auto max-w-3xl; +} + +#oauth-apps .app-container .no-apps-container { + @apply w-full flex h-36 sm:border sm:rounded-md mt-4 mb-20 dark:border-dark-500 border-gray-300; +} + +</style> diff --git a/front-end/src/views/Account/components/oauth/SingleApplication.vue b/front-end/src/views/Account/components/oauth/SingleApplication.vue new file mode 100644 index 0000000..60bad68 --- /dev/null +++ b/front-end/src/views/Account/components/oauth/SingleApplication.vue @@ -0,0 +1,198 @@ +<script setup lang="ts"> +import { toUpper } from 'lodash-es' +import { apiCall, useWait, useConfirm, usePassConfirm, useDataBuffer } from '@vnuge/vnlib.browser' +import { ref, computed, toRefs, watch } from 'vue' +import { get, set, useClipboard, useTimeAgo, useToggle } from '@vueuse/core' +import { getAppValidator } from './o2AppValidation' +import { OAuth2Application } from '../../../../store/oauthAppsPlugin' +import { useStore } from '../../../../store' + +const props = defineProps<{ + application: OAuth2Application + allowEdit: boolean +}>() + +const store = useStore() +const { application, allowEdit } = toRefs(props) + +//Init data buffer around application +const { data, revert, modified, buffer, update, apply } = useDataBuffer(get(application), + async (adb) => { + await store.oauth2.updateAppMeta(adb.buffer); + store.oauth2.refresh(); + }) + +//Watch for store app changes and apply them to the buffer +watch(application, apply) + +const { waiting } = useWait() +const { reveal } = useConfirm() +const { elevatedApiCall } = usePassConfirm() +const { copied, copy } = useClipboard() + +const { v$, validate, reset } = getAppValidator(buffer) + +const [showEdit, toggleEdit] = useToggle() +const newSecret = ref<string | null>(null); + +const name = computed(() => data.name) +const clientId = computed(() => toUpper(data.client_id)) +const createdTime = useTimeAgo(data.Created); +const formId = computed(() => `app-form-${data.client_id}`) + +const onCancel = function () { + revert() + reset() + toggleEdit(false) +} + +const onSubmit = async function () { + // Validate the new app form + if (!await validate()) { + return + } + // Create the new app + await apiCall(async ({ toaster }) => { + // Update does not return anything, if successful + await update() + + toaster.general.success({ + text: 'Application successfully updated', + title: 'Success' + }) + reset() + toggleEdit(false) + }) +} + +const updateSecret = async function () { + // Show a confrimation prompt + const { isCanceled } = await reveal({ + title: 'Update Secret?', + text: `Are you sure you want to update the secret? Any active sessions will be invalidated, and the old secret will be invalidated.` + }) + if (isCanceled) { + return + } + await elevatedApiCall(async ({ password }) => { + // Submit the secret update with the new challenge + newSecret.value = await store.oauth2.updateAppSecret(data, password) + store.oauth2.refresh() + }) +} + +const onDelete = async function () { + // Show a confirmation prompt + const { isCanceled } = await reveal({ + title: 'Delete?', + text: 'You are about to permanently delete this application. This will invalidate any active sessions.', + subtext: '' + }) + if (isCanceled) { + return + } + await elevatedApiCall(async ({ password, toaster }) => { + await store.oauth2.deleteApp(data, password) + toaster.general.success({ + text: 'Application deleted successfully', + title: 'Success' + }) + store.oauth2.refresh() + }) +} + +const closeNewSecret = () => set(newSecret, null); + +</script> + +<template> + <div :id="data.Id"> + <div class="flex flex-row"> + <div class="flex ml-0 mr-auto"> + <div class="flex w-8 h-8 rounded-full bg-primary-500"> + <div class="m-auto text-white dark:text-dark-500"> + <fa-icon icon="key"></fa-icon> + </div> + </div> + <div class="inline my-auto ml-2"> + <h5 class="m-0">{{ name }}</h5> + </div> + </div> + <div v-if="allowEdit && showEdit" class="button-group"> + <button class="btn primary xs" :disabled="!modified" @click="onSubmit">Update</button> + <button class="btn xs" @click="onCancel">Cancel</button> + </div> + <div v-else class=""> + <button class="btn no-border xs" @click="toggleEdit(true)">Edit</button> + </div> + </div> + <div class="px-3 py-1 text-color-background"> + <div class="my-1"> + <span> Client ID: </span> + <span class="font-mono text-color-foreground">{{ clientId }}</span> + </div> + <div class="text-sm"> + <span> Created: </span> + <span>{{ createdTime }}</span> + </div> + <div v-if="!showEdit" class="text-sm"> + <span>{{ data.description }}</span> + </div> + </div> + <div v-if="newSecret" class="flex"> + <div class="py-4 mx-auto"> + <div class="pl-1 mb-2"> + New secret + </div> + <div class="p-4 text-sm break-all border-2 rounded dark:border-dark-500 dark:bg-dark-700"> + {{ newSecret }} + </div> + <div class="flex justify-end my-3"> + <button v-if="!copied" class="rounded btn" @click="copy(newSecret)"> + Copy + </button> + <button v-else class="rounded btn" @click="closeNewSecret"> + Done + </button> + </div> + </div> + </div> + <div v-else-if="showEdit" class="app-form-container"> + <div class="py-4"> + <form :id="formId" class="max-w-md mx-auto"> + <fieldset :disabled="waiting" class=""> + <div class="input-container"> + <div class="pl-1 mb-1"> + App name + </div> + <input class="w-full input primary" :class="{ 'invalid': v$.name.$invalid }" v-model="v$.name.$model" type="text" name="name" /> + </div> + <div class="mt-3 input-container"> + <div class="pl-1 mb-1"> + App description + <span class="text-sm">(optional)</span> + </div> + <textarea class="w-full input primary" :class="{ 'invalid': v$.description.$invalid }" v-model="v$.description.$model" name="description" rows="3" /> + </div> + </fieldset> + </form> + </div> + <div class="mt-3"> + + <div class="mx-auto w-fit"> + <div class="button-group"> + <button class="btn xs" @click.prevent="updateSecret"> + <fa-icon icon="sync" /> + <span class="pl-2">New Secret</span> + </button> + <button class="btn red xs" @click.prevent="onDelete"> + <fa-icon icon="minus-circle" /> + <span class="pl-2">Delete</span> + </button> + </div> + </div> + + </div> + </div> + </div> +</template> diff --git a/front-end/src/views/Account/components/oauth/o2AppValidation.ts b/front-end/src/views/Account/components/oauth/o2AppValidation.ts new file mode 100644 index 0000000..0596f1e --- /dev/null +++ b/front-end/src/views/Account/components/oauth/o2AppValidation.ts @@ -0,0 +1,43 @@ +import { MaybeRef } from 'vue' +import { useVuelidate } from '@vuelidate/core' +import { maxLength, helpers, required } from '@vuelidate/validators' +import { useVuelidateWrapper } from '@vnuge/vnlib.browser' +import { OAuth2Application } from '../../../../store/oauthAppsPlugin' + +//Custom alpha numeric regex +const alphaNumRegex = helpers.regex(/^[a-zA-Z0-9_,\s]*$/) + +const rules = { + name: { + alphaNumSpace: helpers.withMessage("Name contains invalid characters", alphaNumRegex), + maxLength: helpers.withMessage('App name must be less than 50 characters', maxLength(50)), + required: helpers.withMessage('Oauth Application name is required', required) + }, + description: { + alphaNumSpace: helpers.withMessage("Description contains invalid characters", alphaNumRegex), + maxLength: helpers.withMessage('Description must be less than 50 characters', maxLength(50)) + }, + permissions: { + alphaNumSpace: helpers.regex(/^[a-zA-Z0-9_,:\s]*$/), + maxLength: helpers.withMessage('Permissions must be less than 64 characters', maxLength(64)) + } +} + +export interface AppValidator { + readonly v$: ReturnType<typeof useVuelidate> + readonly validate: () => Promise<boolean> + readonly reset: () => void +} + +/** + * Gets the validator for the given application (or new appication) buffer + * @param buffer The app buffer to validate + * @returns The validator instance, validate function, and reset function + */ +export const getAppValidator = (buffer: MaybeRef<OAuth2Application>) : AppValidator => { + //App validator + const v$ = useVuelidate(rules, buffer) + //validate wrapper function + const { validate } = useVuelidateWrapper(v$); + return { v$, validate, reset: v$.value.$reset }; +}
\ No newline at end of file diff --git a/front-end/src/views/Account/components/profile/Profile.vue b/front-end/src/views/Account/components/profile/Profile.vue index 0db7192..106c8b9 100644 --- a/front-end/src/views/Account/components/profile/Profile.vue +++ b/front-end/src/views/Account/components/profile/Profile.vue @@ -1,3 +1,62 @@ + +<script setup lang="ts"> +import { defaultTo } from 'lodash-es' +import { useVuelidate } from '@vuelidate/core' +import { ref, computed, watch, type Ref } from 'vue' +import { Rules, FormSchema } from './profile-schema.ts' +import { apiCall, useMessage, useWait, useVuelidateWrapper, type VuelidateInstance } from '@vnuge/vnlib.browser' +import { useStore } from '../../../../store' + +const { waiting } = useWait() +const { onInput, clearMessage } = useMessage() + +const store = useStore() +const editMode = ref(false) + +// Create validator based on the profile buffer as a data model +const v$ = useVuelidate(Rules, store.userProfile.buffer as any, { $lazy: true }) + +// Setup the validator wrapper +const { validate } = useVuelidateWrapper(v$ as Ref<VuelidateInstance>); + +//const modified = computed(() => profile.value.Modified) +const createdTime = computed(() => defaultTo(store.userProfile.data.created?.toLocaleString(), '')) + +const revertProfile = () => { + //Revert the buffer + store.userProfile.revert() + clearMessage() + editMode.value = false +} + +const onSubmit = async () => { + if (waiting.value) { + return; + } + // Validate the form + if (!await validate()) { + return + } + // Init the api call + await apiCall(async ({ toaster }) => { + const res = await store.userProfile.update(); + + const successm = res.getResultOrThrow(); + + //No longer in edit mode + editMode.value = false + + //Show success message + toaster.general.success({ + title: 'Update successful', + text: successm, + }) + }) +} + +watch(editMode, () => v$.value.$reset()) + +</script> <template> <div id="account-profile" class="acnt-content-container panel-container"> <div class="acnt-content profile-container panel-content"> @@ -57,66 +116,6 @@ </div> </template> -<script setup lang="ts"> -import { defaultTo } from 'lodash-es' -import useVuelidate from '@vuelidate/core' -import { ref, computed, watch } from 'vue' -import { Rules, FormSchema } from './profile-schema.ts' -import { apiCall, useMessage, useWait, useVuelidateWrapper, WebMessage } from '@vnuge/vnlib.browser' -import { useStore } from '../../../../store' - -const { waiting } = useWait() -const { onInput, clearMessage } = useMessage() - -const store = useStore() - -const editMode = ref(false) - -// Create validator based on the profile buffer as a data model -const v$ = useVuelidate(Rules, store.userProfile.buffer, { $lazy:true }) - -// Setup the validator wrapper -const { validate } = useVuelidateWrapper(v$); - -//const modified = computed(() => profile.value.Modified) -const createdTime = computed(() => defaultTo(store.userProfile.data.created?.toLocaleString(), '')) - -const revertProfile = () => { - //Revert the buffer - store.userProfile.revert() - clearMessage() - editMode.value = false -} - -const onSubmit = async () => { - if(waiting.value){ - return; - } - // Validate the form - if (!await validate()) { - return - } - // Init the api call - await apiCall(async ({ toaster }) => { - const res = await store.userProfile.update(); - - const successm = (res as WebMessage<string>).getResultOrThrow(); - - //No longer in edit mode - editMode.value = false - - //Show success message - toaster.general.success({ - title: 'Update successful', - text: successm, - }) - }) -} - -watch(editMode, () => v$.value.$reset()) - - -</script> <style lang="scss"> diff --git a/front-end/src/views/Account/components/settings/Fido.vue b/front-end/src/views/Account/components/settings/Fido.vue index d453378..9303541 100644 --- a/front-end/src/views/Account/components/settings/Fido.vue +++ b/front-end/src/views/Account/components/settings/Fido.vue @@ -1,3 +1,19 @@ +<script setup lang="ts"> +import { useSession } from '@vnuge/vnlib.browser' +import { toRefs } from 'vue'; + +const props = defineProps<{ + fidoEnabled?: boolean +}>() + +const { fidoEnabled } = toRefs(props) +const { isLocalAccount } = useSession() + +const Disable = () => { } +const Setup = () => { } + +</script> + <template> <div id="account-fido-settings"> <div v-if="!isLocalAccount" class="flex flex-row justify-between"> @@ -30,24 +46,3 @@ </div> </div> </template> - -<script setup lang="ts"> -import { useSession } from '@vnuge/vnlib.browser' -import { toRefs } from 'vue'; - -const props = defineProps<{ - fidoEnabled?: boolean -}>() - -const { fidoEnabled } = toRefs(props) - -const { isLocalAccount } = useSession() - -const Disable = () => {} -const Setup = () => {} - -</script> - -<style> - -</style> diff --git a/front-end/src/views/Account/components/settings/PasswordReset.vue b/front-end/src/views/Account/components/settings/PasswordReset.vue index 24dced6..61fda7d 100644 --- a/front-end/src/views/Account/components/settings/PasswordReset.vue +++ b/front-end/src/views/Account/components/settings/PasswordReset.vue @@ -1,68 +1,9 @@ -<template> - <div id="pwreset-settings" class="container"> - <div class="panel-content"> - - <div v-if="!pwResetShow" class=""> - <div class="flex flex-wrap items-center justify-between"> - - <div class=""> - <h5>Password Reset</h5> - </div> - - <div class="flex justify-end"> - <button class="btn xs" @click="showForm"> - <fa-icon icon="sync" /> - <span class="pl-2">Reset Password</span> - </button> - </div> - </div> - - <p class="mt-3 text-sm text-color-background"> - You may only reset your password if you have an internal user account. If you exclusivly use an external - authentication provider (like GitHub or Discord), you will need to reset your password externally. - </p> - </div> - - <div v-else class="px-2 my-2"> - - <p class="my-3 text-center"> - Enter your current password, new password, and confirm the new password. - </p> - - <dynamic-form - id="password-reset-form" - class="pwreset-form primary" - :form="formSchema" - :disabled="waiting" - :validator="v$" - @submit="onSubmit" - @input="onInput" - /> - - <div class="flex flex-row justify-end my-2"> - <div class="button-group"> - <button type="submit" form="password-reset-form" class="btn primary sm" :disabled="waiting"> - <fa-icon v-if="!waiting" icon="check" /> - <fa-icon v-else class="animate-spin" icon="spinner" /> - Update - </button> - <button class="btn sm cancel-btn" :disabled="waiting" @click="resetForm"> - Cancel - </button> - </div> - </div> - - </div> - </div> - </div> -</template> - <script setup lang="ts"> import { isEmpty, toSafeInteger } from 'lodash-es'; import { useVuelidate } from '@vuelidate/core' import { required, maxLength, minLength, helpers } from '@vuelidate/validators' -import { useUser, apiCall, useMessage, useWait, useConfirm, useVuelidateWrapper } from '@vnuge/vnlib.browser' -import { computed, reactive, ref, toRefs, watch } from 'vue' +import { useUser, apiCall, useMessage, useWait, useConfirm, useVuelidateWrapper, VuelidateInstance } from '@vnuge/vnlib.browser' +import { MaybeRef, computed, reactive, ref, toRefs, watch } from 'vue' const props = defineProps<{ totpEnabled: boolean, @@ -136,7 +77,7 @@ const rules = computed(() =>{ }) const v$ = useVuelidate(rules, vState, { $lazy: true }) -const { validate } = useVuelidateWrapper(v$) +const { validate } = useVuelidateWrapper(v$ as MaybeRef<VuelidateInstance>) const showTotpCode = computed(() => totpEnabled.value && !fidoEnabled.value) @@ -208,6 +149,58 @@ const resetForm = () => { </script> +<template> + <div id="pwreset-settings" class="container"> + <div class="panel-content"> + + <div v-if="!pwResetShow" class=""> + <div class="flex flex-wrap items-center justify-between"> + + <div class=""> + <h5>Password Reset</h5> + </div> + + <div class="flex justify-end"> + <button class="btn xs" @click="showForm"> + <fa-icon icon="sync" /> + <span class="pl-2">Reset Password</span> + </button> + </div> + </div> + + <p class="mt-3 text-sm text-color-background"> + You may only reset your password if you have an internal user account. If you exclusivly use an external + authentication provider (like GitHub or Discord), you will need to reset your password externally. + </p> + </div> + + <div v-else class="px-2 my-2"> + + <p class="my-3 text-center"> + Enter your current password, new password, and confirm the new password. + </p> + + <dynamic-form id="password-reset-form" class="pwreset-form primary" :form="formSchema" :disabled="waiting" + :validator="v$" @submit="onSubmit" @input="onInput" /> + + <div class="flex flex-row justify-end my-2"> + <div class="button-group"> + <button type="submit" form="password-reset-form" class="btn primary sm" :disabled="waiting"> + <fa-icon v-if="!waiting" icon="check" /> + <fa-icon v-else class="animate-spin" icon="spinner" /> + Update + </button> + <button class="btn sm cancel-btn" :disabled="waiting" @click="resetForm"> + Cancel + </button> + </div> + </div> + + </div> + </div> + </div> +</template> + <style lang="scss"> #password-reset-form{ diff --git a/front-end/src/views/Account/components/settings/Pki.vue b/front-end/src/views/Account/components/settings/Pki.vue index afe606f..957a188 100644 --- a/front-end/src/views/Account/components/settings/Pki.vue +++ b/front-end/src/views/Account/components/settings/Pki.vue @@ -1,128 +1,35 @@ -<template> - <div id="pki-settings" class="container"> - <div class="panel-content"> - - <div class="flex flex-row flex-wrap justify-between"> - <h5>PKI Authentication</h5> - <div class=""> - <div v-if="pkiEnabled" class="button-group"> - <button class="btn xs" @click.prevent="setIsOpen(true)"> - <fa-icon icon="plus" /> - <span class="pl-2">Add Key</span> - </button> - <button class="btn red xs" @click.prevent="onDisable"> - <fa-icon icon="minus-circle" /> - <span class="pl-2">Disable</span> - </button> - </div> - <div v-else class=""> - <button class="btn primary xs" @click.prevent="setIsOpen(true)"> - <fa-icon icon="plus" /> - <span class="pl-2">Add Key</span> - </button> - </div> - </div> - - <div v-if="store.pkiPublicKeys && store.pkiPublicKeys.length > 0" class="w-full mt-4"> - <table class="min-w-full text-sm divide-y-2 divide-gray-200 dark:divide-dark-500"> - <thead class="text-left"> - <tr> - <th class="p-2 font-medium whitespace-nowrap dark:text-white" > - KeyID - </th> - <th class="p-2 font-medium whitespace-nowrap dark:text-white"> - Algorithm - </th> - <th class="p-2 font-medium whitespace-nowrap dark:text-white"> - Curve - </th> - <th class="p-2"></th> - </tr> - </thead> - - <tbody class="divide-y divide-gray-200 dark:divide-dark-500"> - <tr v-for="key in store.pkiPublicKeys"> - <td class="p-2 t font-medium truncate max-w-[8rem] whitespace-nowrap dark:text-white"> - {{ key.kid }} - </td> - <td class="p-2 text-gray-700 whitespace-nowrap dark:text-gray-200"> - {{ key.alg }} - </td> - <td class="p-2 text-gray-700 whitespace-nowrap dark:text-gray-200"> - {{ key.crv }} - </td> - <td class="p-2 text-right whitespace-nowrap"> - <button class="rounded btn red xs borderless" @click="onRemoveKey(key)"> - <span class="hidden sm:inline">Remove</span> - <fa-icon icon="trash-can" class="inline sm:hidden" /> - </button> - </td> - </tr> - </tbody> - </table> - </div> - - <p v-else class="p-1 pt-3 text-sm text-color-background"> - PKI authentication is a method of authenticating your user account with signed messages and a shared public key. This method implementation - uses client signed Json Web Tokens to authenticate user generated outside this website as a One Time Password (OTP). This allows for you to - use your favorite hardware or software tools, to generate said OTPs to authenticate your user. - </p> - </div> - </div> - </div> - <Dialog :open="isOpen" @close="setIsOpen" class="relative z-30"> - <div class="fixed inset-0 bg-black/30" aria-hidden="true" /> - - <div class="fixed inset-0 flex justify-center"> - <DialogPanel class="w-full max-w-lg p-4 m-auto mt-24 bg-white rounded dark:bg-dark-700 dark:text-gray-300"> - <h4>Configure your authentication key</h4> - <p class="mt-2 text-sm"> - Please paste your authenticator's public key as a Json Web Key (JWK) object. Your JWK must include a kid (key id) and a kty (key type) field. - </p> - <div class="p-2 mt-3"> - <textarea class="w-full p-1 text-sm border dark:bg-dark-700 ring-0 dark:border-dark-400" rows="10" v-model="keyData" /> - </div> - <div class="flex justify-end gap-2 mt-4"> - <button class="rounded btn sm primary" @click.prevent="onSubmitKeys">Submit</button> - <button class="rounded btn sm" @click.prevent="setIsOpen(false)">Cancel</button> - </div> - </DialogPanel> - </div> - </Dialog> -</template> - <script setup lang="ts"> import { includes, isEmpty } from 'lodash-es' -import { apiCall, useConfirm, useSession, debugLog, useFormToaster, PkiPublicKey } from '@vnuge/vnlib.browser' +import { apiCall, useConfirm, useSession, debugLog, useFormToaster, type PkiPublicKey, MfaMethod } from '@vnuge/vnlib.browser' import { computed, ref, watch } from 'vue' import { Dialog, DialogPanel } from '@headlessui/vue' import { useStore } from '../../../../store' -import { } from 'pinia' +import { useToggle } from '@vueuse/core' const store = useStore() const { reveal } = useConfirm() const { isLocalAccount } = useSession() const { error } = useFormToaster() +const { refresh, pkiConfig } = store.pki! -const pkiEnabled = computed(() => isLocalAccount.value && includes(store.mfaEndabledMethods, "pki") && window.crypto.subtle) +const pkiEnabled = computed(() => isLocalAccount.value && includes(store.mfa.enabledMethods, "pki" as MfaMethod) && window.crypto.subtle) +const pkiPublicKeys = computed(() => store.pki!.publicKeys) -const isOpen = ref(false) +const [isOpen, toggleOpen] = useToggle() const keyData = ref('') const pemFormat = ref(false) const explicitCurve = ref("") -watch(isOpen, () =>{ +watch(isOpen, () => { keyData.value = '' pemFormat.value = false explicitCurve.value = "" //Reload status - store.mfaRefreshMethods() + refresh() }) -const setIsOpen = (value : boolean) => isOpen.value = value - -const onRemoveKey = async (single: PkiPublicKey) =>{ - const { isCanceled } = await reveal({ +const onRemoveKey = async (single: PkiPublicKey) => { + const { isCanceled } = await reveal({ title: 'Are you sure?', text: `This will remove key ${single.kid} from your account.` }) @@ -130,11 +37,11 @@ const onRemoveKey = async (single: PkiPublicKey) =>{ return; } - //Delete pki + //Delete pki await apiCall(async ({ toaster }) => { - + //TODO: require password or some upgrade to disable - const { success } = await store.pkiConfig.removeKey(single.kid); + const { success } = await pkiConfig.removeKey(single.kid); if (success) { toaster.general.success({ @@ -150,33 +57,33 @@ const onRemoveKey = async (single: PkiPublicKey) =>{ } //Refresh the status - store.mfaRefreshMethods() + refresh() }); } const onDisable = async () => { - const { isCanceled } = await reveal({ + const { isCanceled } = await reveal({ title: 'Are you sure?', text: 'This will disable PKI authentication for your account.' }) if (isCanceled) { - return; + return; } //Delete pki - await apiCall(async ({ toaster }) =>{ + await apiCall(async ({ toaster }) => { //Disable pki //TODO: require password or some upgrade to disable - const { success } = await store.pkiConfig.disable(); - - if(success){ + const { success } = await pkiConfig.disable(); + + if (success) { toaster.general.success({ title: 'Success', text: 'PKI authentication has been disabled.' }) } - else{ + else { toaster.general.error({ title: 'Error', text: 'PKI authentication could not be disabled.' @@ -184,40 +91,40 @@ const onDisable = async () => { } //Refresh the status - store.mfaRefreshMethods() + refresh() }); } -const onSubmitKeys = async () =>{ - - if(window.crypto.subtle == null){ +const onSubmitKeys = async () => { + + if (window.crypto.subtle == null) { error({ title: "Your browser does not support PKI authentication." }) return; } - + //Validate key data - if(isEmpty(keyData.value)){ + if (isEmpty(keyData.value)) { error({ title: "Please enter key data" }) return; } - let jwk : PkiPublicKey & JsonWebKey; + let jwk: PkiPublicKey & JsonWebKey; try { //Try to parse as jwk jwk = JSON.parse(keyData.value) - if(isEmpty(jwk.use) - || isEmpty(jwk.kty) - || isEmpty(jwk.alg) - || isEmpty(jwk.kid) - || isEmpty(jwk.x) - || isEmpty(jwk.y)){ + if (isEmpty(jwk.use) + || isEmpty(jwk.kty) + || isEmpty(jwk.alg) + || isEmpty(jwk.kid) + || isEmpty(jwk.x) + || isEmpty(jwk.y)) { throw new Error("Invalid JWK"); } } catch (e) { //Write error to debug log debugLog(e) - error({ title:"The key is not a valid Json Web Key (JWK)"}) + error({ title: "The key is not a valid Json Web Key (JWK)" }) return; } @@ -226,7 +133,7 @@ const onSubmitKeys = async () =>{ //init/update the key //TODO: require password or some upgrade to disable - const { getResultOrThrow } = await store.pkiConfig.addOrUpdate(jwk); + const { getResultOrThrow } = await pkiConfig.addOrUpdate(jwk); const result = getResultOrThrow(); @@ -234,12 +141,103 @@ const onSubmitKeys = async () =>{ title: 'Success', text: result }) - setIsOpen(false) + toggleOpen(false) }) } </script> -<style> +<template> + <div id="pki-settings" class="container"> + <div class="panel-content"> + + <div class="flex flex-row flex-wrap justify-between"> + <h5>PKI Authentication</h5> + <div class=""> + <div v-if="pkiEnabled" class="button-group"> + <button class="btn xs" @click.prevent="toggleOpen(true)"> + <fa-icon icon="plus" /> + <span class="pl-2">Add Key</span> + </button> + <button class="btn red xs" @click.prevent="onDisable"> + <fa-icon icon="minus-circle" /> + <span class="pl-2">Disable</span> + </button> + </div> + <div v-else class=""> + <button class="btn primary xs" @click.prevent="toggleOpen(true)"> + <fa-icon icon="plus" /> + <span class="pl-2">Add Key</span> + </button> + </div> + </div> + + <div v-if="pkiPublicKeys && pkiPublicKeys.length > 0" class="w-full mt-4"> + <table class="min-w-full text-sm divide-y-2 divide-gray-200 dark:divide-dark-500"> + <thead class="text-left"> + <tr> + <th class="p-2 font-medium whitespace-nowrap dark:text-white" > + KeyID + </th> + <th class="p-2 font-medium whitespace-nowrap dark:text-white"> + Algorithm + </th> + <th class="p-2 font-medium whitespace-nowrap dark:text-white"> + Curve + </th> + <th class="p-2"></th> + </tr> + </thead> + + <tbody class="divide-y divide-gray-200 dark:divide-dark-500"> + <tr v-for="key in pkiPublicKeys"> + <td class="p-2 t font-medium truncate max-w-[8rem] whitespace-nowrap dark:text-white"> + {{ key.kid }} + </td> + <td class="p-2 text-gray-700 whitespace-nowrap dark:text-gray-200"> + {{ key.alg }} + </td> + <td class="p-2 text-gray-700 whitespace-nowrap dark:text-gray-200"> + {{ key.crv }} + </td> + <td class="p-2 text-right whitespace-nowrap"> + <button class="rounded btn red xs borderless" @click="onRemoveKey(key)"> + <span class="hidden sm:inline">Remove</span> + <fa-icon icon="trash-can" class="inline sm:hidden" /> + </button> + </td> + </tr> + </tbody> + </table> + </div> + + <p v-else class="p-1 pt-3 text-sm text-color-background"> + PKI authentication is a method of authenticating your user account with signed messages and a shared public key. This method implementation + uses client signed Json Web Tokens to authenticate user generated outside this website as a One Time Password (OTP). This allows for you to + use your favorite hardware or software tools, to generate said OTPs to authenticate your user. + </p> + </div> + </div> + </div> + <Dialog :open="isOpen" @close="toggleOpen(false)" class="relative z-30"> + <div class="fixed inset-0 bg-black/30" aria-hidden="true" /> + + <div class="fixed inset-0 flex justify-center"> + <DialogPanel class="w-full max-w-lg p-4 m-auto mt-24 bg-white rounded dark:bg-dark-700 dark:text-gray-300"> + <h4>Configure your authentication key</h4> + <p class="mt-2 text-sm"> + Please paste your authenticator's public key as a Json Web Key (JWK) object. Your JWK must include a kid (key id) and a kty (key type) field. + </p> + <div class="p-2 mt-3"> + <textarea class="w-full p-1 text-sm border dark:bg-dark-700 ring-0 dark:border-dark-400" rows="10" v-model="keyData" /> + </div> + <div class="flex justify-end gap-2 mt-4"> + <button class="rounded btn sm primary" @click.prevent="onSubmitKeys">Submit</button> + <button class="rounded btn sm" @click.prevent="toggleOpen(false)">Cancel</button> + </div> + </DialogPanel> + </div> + </Dialog> +</template> + -</style> diff --git a/front-end/src/views/Account/components/settings/Security.vue b/front-end/src/views/Account/components/settings/Security.vue index e6075f9..ae0d143 100644 --- a/front-end/src/views/Account/components/settings/Security.vue +++ b/front-end/src/views/Account/components/settings/Security.vue @@ -1,3 +1,25 @@ +<script setup lang="ts"> +import { MfaMethod } from '@vnuge/vnlib.browser' +import { computed } from 'vue' +import { Switch } from '@headlessui/vue' +import { includes, isNil } from 'lodash-es' +import { useStore } from '../../../../store' +import Fido from './Fido.vue' +import Pki from './Pki.vue' +import TotpSettings from './TotpSettings.vue' +import PasswordReset from './PasswordReset.vue' + +const store = useStore(); + +//Load mfa methods +store.mfa.refresh(); + +const fidoEnabled = computed(() => includes(store.mfa.enabledMethods, 'fido' as MfaMethod)) +const totpEnabled = computed(() => includes(store.mfa.enabledMethods, MfaMethod.TOTP)) +const pkiEnabled = computed(() => !isNil(store.pki)) + +</script> + <template> <div id="account-security-settings"> <div class="panel-container"> @@ -20,20 +42,20 @@ </div> </div> - <Pki /> + <Pki v-if="pkiEnabled" /> <div id="browser-poll-settings" class="panel-content" > <div class="flex justify-between"> <h5>Keep me logged in</h5> <div class="pl-1"> <Switch - v-model="autoHeartbeat" - :class="autoHeartbeat ? 'bg-primary-500 dark:bg-primary-600' : 'bg-gray-200 dark:bg-dark-500'" + v-model="store.autoHeartbeat" + :class="store.autoHeartbeat ? 'bg-primary-500 dark:bg-primary-600' : 'bg-gray-200 dark:bg-dark-500'" class="relative inline-flex items-center h-6 rounded-full w-11" > <span class="sr-only">Enable auto heartbeat</span> <span - :class="autoHeartbeat ? 'translate-x-6' : 'translate-x-1'" + :class="store.autoHeartbeat ? 'translate-x-6' : 'translate-x-1'" class="inline-block w-4 h-4 transition transform bg-white rounded-full" /> </Switch> @@ -51,29 +73,6 @@ </div> </template> -<script setup lang="ts"> -import { MfaMethod } from '@vnuge/vnlib.browser' -import { computed } from 'vue' -import { Switch } from '@headlessui/vue' -import { includes } from 'lodash-es' -import { storeToRefs } from 'pinia' -import { useStore } from '../../../../store' -import Fido from './Fido.vue' -import Pki from './Pki.vue' -import TotpSettings from './TotpSettings.vue' -import PasswordReset from './PasswordReset.vue' - -const store = useStore(); -const { autoHeartbeat } = storeToRefs(store); - -//Load mfa methods -store.mfaRefreshMethods(); - -const fidoEnabled = computed(() => includes(store.mfaEndabledMethods, 'fido' as MfaMethod)) -const totpEnabled = computed(() => includes(store.mfaEndabledMethods, MfaMethod.TOTP)) - -</script> - <style> #account-security-settings .modal-body{ diff --git a/front-end/src/views/Account/components/settings/Settings.vue b/front-end/src/views/Account/components/settings/Settings.vue index fb86951..0580b58 100644 --- a/front-end/src/views/Account/components/settings/Settings.vue +++ b/front-end/src/views/Account/components/settings/Settings.vue @@ -1,3 +1,7 @@ +<script setup lang="ts"> +import Security from './Security.vue' +</script> + <template> <div id="account-settings" class="container"> <div class="acnt-content-container"> @@ -5,12 +9,3 @@ </div> </div> </template> - -<script setup lang="ts"> -import Security from './Security.vue' - -</script> - -<style> - -</style> diff --git a/front-end/src/views/Account/components/settings/TotpSettings.vue b/front-end/src/views/Account/components/settings/TotpSettings.vue index 0fcfe31..04a261b 100644 --- a/front-end/src/views/Account/components/settings/TotpSettings.vue +++ b/front-end/src/views/Account/components/settings/TotpSettings.vue @@ -1,99 +1,11 @@ -<template> - <div id="totp-settings"> - - <div v-if="!isLocalAccount" class="flex flex-row justify-between"> - <h6 class="block"> - TOTP Authenticator App - </h6> - <div class="text-red-500"> - Unavailable for external auth - </div> - </div> - - <div v-else-if="showTotpCode" class="w-full py-2 text-center"> - <h5 class="text-center" /> - <p class="py-2"> - Scan the QR code with your TOTP authenticator app. - </p> - - <div class="flex"> - <VueQrcode class="m-auto" :value="qrCode" /> - </div> - - <p class="py-2"> - Your secret, if your application requires it. - </p> - - <p class="flex flex-row flex-wrap justify-center p-2 bg-gray-200 border border-gray-300 dark:bg-dark-800 dark:border-dark-500"> - <span v-for="code in secretSegments" :key="code" class="px-2 font-mono tracking-wider" > - {{ code }} - </span> - </p> - - <p class="py-2 text-color-background"> - Please enter your code from your authenticator app to continue. - </p> - - <div class="m-auto w-min"> - <VOtpInput - class="otp-input" - input-type="letter-numeric" - separator="" - :is-disabled="showSubmitButton" - input-classes="primary input rounded" - :num-inputs="6" - @on-change="onInput" - @on-complete="VerifyTotp" - /> - </div> - - <div v-if="showSubmitButton" class="flex flex-row justify-end my-2"> - <button class="btn primary" @click.prevent="CloseQrWindow"> - Complete - </button> - </div> - </div> - - <div v-else class="flex flex-row flex-wrap justify-between"> - <h6>TOTP Authenticator App</h6> - - <div v-if="totpEnabled" class="button-group"> - <button class="btn xs" @click.prevent="regenTotp"> - <fa-icon icon="sync" /> - <span class="pl-2">Regenerate</span> - </button> - <button class="btn red xs" @click.prevent="disable"> - <fa-icon icon="minus-circle" /> - <span class="pl-2">Disable</span> - </button> - </div> - - <div v-else> - <button class="btn primary xs" @click.prevent="configTotp"> - <fa-icon icon="plus" /> - <span class="pl-2">Setup</span> - </button> - </div> - <p class="p-1 pt-3 text-sm text-color-background"> - TOTP is a time based one time password. You can use it as a form of Multi Factor Authentication when - using another device such as a smart phone or TOTP hardware device. You can use TOTP with your smart - phone - using apps like Google Authenticator, Authy, or Duo. Read more on - <a class="link" href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank"> - Wikipedia. - </a> - </p> - </div> - - </div> -</template> - <script setup lang="ts"> import { isNil, chunk, defaultTo, includes, map, join } from 'lodash-es' import { TOTP } from 'otpauth' -import { computed, ref, defineAsyncComponent } from 'vue' import base32Encode from 'base32-encode' -import { +import QrCodeVue from 'qrcode.vue' +import VOtpInput from "vue3-otp-input"; +import { computed, ref } from 'vue' +import { useSession, useMessage, useConfirm, @@ -104,26 +16,23 @@ import { import { useStore } from '../../../../store'; import { storeToRefs } from 'pinia'; -const VueQrcode = defineAsyncComponent(() => import('@chenfengyuan/vue-qrcode')) -const VOtpInput = defineAsyncComponent(() => import('vue3-otp-input')); - -interface TotpConfig{ - secret: string; - readonly issuer: string; - readonly algorithm: string; - readonly digits?: number; - readonly period?: number; +interface TotpConfig { + secret: string; + readonly issuer: string; + readonly algorithm: string; + readonly digits?: number; + readonly period?: number; } const store = useStore(); -const { userName, isLocalAccount, mfaEndabledMethods } = storeToRefs(store); +const { isLocalAccount } = storeToRefs(store); const { KeyStore } = useSession() const { reveal } = useConfirm() const { elevatedApiCall } = usePassConfirm() const { onInput, setMessage } = useMessage() -const totpEnabled = computed(() => includes(mfaEndabledMethods.value, MfaMethod.TOTP)) +const totpEnabled = computed(() => includes(store.mfa.enabledMethods, MfaMethod.TOTP)) const totpMessage = ref<TotpConfig>() const showSubmitButton = ref(false) @@ -152,7 +61,7 @@ const qrCode = computed(() => { params.append('algorithm', m.algorithm) params.append('digits', defaultTo(m.digits, 6).toString()) params.append('period', defaultTo(m.period, 30).toString()) - const url = `otpauth://totp/${m.issuer}:${userName.value}?${params.toString()}` + const url = `otpauth://totp/${m.issuer}:${store.userName}?${params.toString()}` return url }) @@ -160,7 +69,7 @@ const ProcessAddOrUpdate = async () => { await elevatedApiCall(async ({ password }) => { // Init or update the totp method and get the encrypted totp message - const res = await store.mfaConfig.initOrUpdateMethod<TotpConfig>(MfaMethod.TOTP, password); + const res = await store.mfa.initOrUpdateMethod<TotpConfig>(MfaMethod.TOTP, password); //Get the encrypted totp message const totp = res.getResultOrThrow() @@ -181,7 +90,7 @@ const configTotp = async () => { text: 'Are you sure you understand TOTP multi factor and wish to enable it?', }) - if(!isCanceled){ + if (!isCanceled) { ProcessAddOrUpdate() } } @@ -197,7 +106,7 @@ const regenTotp = async () => { text: 'If you continue your previous TOTP authenticator and recovery codes will no longer be valid.' }) - if(!isCanceled){ + if (!isCanceled) { ProcessAddOrUpdate() } } @@ -214,16 +123,15 @@ const disable = async () => { } await elevatedApiCall(async ({ password }) => { - // Disable the totp method - const res = await store.mfaConfig.disableMethod(MfaMethod.TOTP, password) + const res = await store.mfa.disableMethod(MfaMethod.TOTP, password) res.getResultOrThrow() - - store.mfaRefreshMethods() + + store.mfa.refresh() }) } -const VerifyTotp = async (code : string) => { +const VerifyTotp = async (code: string) => { // Create a new TOTP instance from the current message const totp = new TOTP(totpMessage.value) @@ -231,10 +139,11 @@ const VerifyTotp = async (code : string) => { const valid = totp.validate({ token: code, window: 4 }) if (valid) { - showSubmitButton.value = true + showSubmitButton.value = true; + toaster.success({ title: 'Success', - text: 'Your TOTP code is valid and your account is now verified.' + text: 'Your TOTP code is valid and is now enabled' }) } else { setMessage('Your TOTP code is not valid.') @@ -244,12 +153,103 @@ const VerifyTotp = async (code : string) => { const CloseQrWindow = () => { showSubmitButton.value = false totpMessage.value = undefined - + //Fresh methods - store.mfaRefreshMethods() + store.mfa.refresh() } </script> +<template> + <div id="totp-settings"> + + <div v-if="!isLocalAccount" class="flex flex-row justify-between"> + <h6 class="block"> + TOTP Authenticator App + </h6> + <div class="text-red-500"> + Unavailable for external auth + </div> + </div> + + <div v-else-if="showTotpCode" class="w-full py-2 text-center"> + <h5 class="text-center" /> + <p class="py-2"> + Scan the QR code with your TOTP authenticator app. + </p> + + <div class="flex"> + <QrCodeVue class="m-auto" :size="180" render-as="svg" level="Q" :value="qrCode" /> + </div> + + <p class="py-2"> + Your secret, if your application requires it. + </p> + + <p class="flex flex-row flex-wrap justify-center p-2 bg-gray-200 border border-gray-300 dark:bg-dark-800 dark:border-dark-500"> + <span v-for="code in secretSegments" :key="code" class="px-2 font-mono tracking-wider" > + {{ code }} + </span> + </p> + + <p class="py-2 text-color-background"> + Please enter your code from your authenticator app to continue. + </p> + + <div class="m-auto w-min"> + <VOtpInput + class="otp-input" + input-type="letter-numeric" + separator="" + value="" + :is-disabled="showSubmitButton" + input-classes="primary input rounded" + :num-inputs="6" + @on-change="onInput" + @on-complete="VerifyTotp" + /> + </div> + + <div v-if="showSubmitButton" class="flex flex-row justify-end my-2"> + <button class="btn primary" @click.prevent="CloseQrWindow"> + Complete + </button> + </div> + </div> + + <div v-else class="flex flex-row flex-wrap justify-between"> + <h6>TOTP Authenticator App</h6> + + <div v-if="totpEnabled" class="button-group"> + <button class="btn xs" @click.prevent="regenTotp"> + <fa-icon icon="sync" /> + <span class="pl-2">Regenerate</span> + </button> + <button class="btn red xs" @click.prevent="disable"> + <fa-icon icon="minus-circle" /> + <span class="pl-2">Disable</span> + </button> + </div> + + <div v-else> + <button class="btn primary xs" @click.prevent="configTotp"> + <fa-icon icon="plus" /> + <span class="pl-2">Setup</span> + </button> + </div> + <p class="p-1 pt-3 text-sm text-color-background"> + TOTP is a time based one time password. You can use it as a form of Multi Factor Authentication when + using another device such as a smart phone or TOTP hardware device. You can use TOTP with your smart + phone + using apps like Google Authenticator, Authy, or Duo. Read more on + <a class="link" href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank"> + Wikipedia. + </a> + </p> + </div> + + </div> +</template> + <style> diff --git a/front-end/src/views/Blog/components/Posts.vue b/front-end/src/views/Blog/components/Posts.vue index 647e093..b89d263 100644 --- a/front-end/src/views/Blog/components/Posts.vue +++ b/front-end/src/views/Blog/components/Posts.vue @@ -1,24 +1,3 @@ -<template> - <div id="post-editor" class=""> - <EditorTable title="Manage posts" :show-edit="showEdit" :pagination="pagination" @open-new="openNew"> - <template #table> - <PostTable - :items="items" - @open-edit="openEdit" - @delete="onDelete" - /> - </template> - <template #editor> - <PostEditor - @submit="onSubmit" - @close="closeEdit(true)" - @delete="onDelete" - /> - </template> - </EditorTable> - </div> -</template> - <script setup lang="ts"> import { computed, defineAsyncComponent } from 'vue'; import { isEmpty } from 'lodash-es'; @@ -49,7 +28,7 @@ const closeEdit = (update?: boolean) => { //reload channels if (update) { //must refresh content and posts when a post is updated - refresh(); + refresh(); } //Reset page to top window.scrollTo(0, 0); @@ -62,14 +41,14 @@ const openNew = () => { window.scrollTo(0, 0) } -const onSubmit = async ({post, content } : { post: PostMeta, content: string }) => { +const onSubmit = async ({ post, content }: { post: PostMeta, content: string }) => { debugLog('submitting', post, content); //Check for new channel, or updating old channel if (store.posts.selectedId === 'new') { //Exec create call - await apiCall(async ({toaster}) => { + await apiCall(async ({ toaster }) => { //endpoint returns the content const newMeta = await store.posts.add(post); @@ -86,9 +65,9 @@ const onSubmit = async ({post, content } : { post: PostMeta, content: string }) } else if (!isEmpty(store.posts.selectedId)) { //Exec update call - await apiCall(async ( {toaster} ) => { + await apiCall(async ({ toaster }) => { await store.posts.update(post); - + //Publish the content await store.content.updatePostContent(post, content) @@ -129,6 +108,26 @@ const onDelete = async (post: PostMeta) => { } </script> +<template> + <div id="post-editor" class=""> + <EditorTable title="Manage posts" :show-edit="showEdit" :pagination="pagination" @open-new="openNew"> + <template #table> + <PostTable + :items="items" + @open-edit="openEdit" + @delete="onDelete" + /> + </template> + <template #editor> + <PostEditor + @submit="onSubmit" + @close="closeEdit(true)" + @delete="onDelete" + /> + </template> + </EditorTable> + </div> +</template> <style lang="scss"> diff --git a/front-end/src/views/Blog/components/image-preview-dialog.vue b/front-end/src/views/Blog/components/image-preview-dialog.vue index 5cfe552..b134d19 100644 --- a/front-end/src/views/Blog/components/image-preview-dialog.vue +++ b/front-end/src/views/Blog/components/image-preview-dialog.vue @@ -1,34 +1,14 @@ -<template> - <div class=""> - <Dialog :open="isOpen" @close="onClose" class="relative z-50"> - <!-- The backdrop, rendered as a fixed sibling to the panel container --> - <div class="fixed inset-0 bg-black/30" aria-hidden="true" /> - - <!-- Full-screen container to center the panel --> - <div class="fixed inset-0 flex items-center justify-center w-screen p-4"> - <!-- The actual dialog panel --> - <DialogPanel class="p-2 bg-white rounded dark:bg-dark-700" ref="dialog"> - <DialogDescription> - <img class="preview-image" :src="imgUrl" alt="preview" /> - </DialogDescription> - <!-- ... --> - </DialogPanel> - </div> - </Dialog> - </div> -</template> - <script setup lang="ts"> import { ref, computed, watch, toRefs } from 'vue' import { Dialog, DialogDescription } from '@headlessui/vue' -import { onClickOutside } from '@vueuse/core'; +import { onClickOutside, set } from '@vueuse/core'; import { useStore } from '../../../store'; import { ContentMeta } from '../../../../../lib/admin/dist'; import { isNil } from 'lodash-es'; import { apiCall } from '@vnuge/vnlib.browser'; const emit = defineEmits(['close']) -const props = defineProps<{ +const props = defineProps<{ item: ContentMeta | undefined, }>() @@ -52,19 +32,33 @@ const downloadImage = (item: ContentMeta) => { }) } -//load the image when open -watch(item, (item) => { - if (isNil(item)) { - imgUrl.value = undefined - } else { - downloadImage(item) - } -}) +//load the image when open or remove it if the item is undefined +watch(item, (item) => isNil(item) ? set(imgUrl, undefined) : downloadImage(item)) //Close dialog when clicking outside onClickOutside(dialog, onClose) </script> + +<template> + <div class=""> + <Dialog :open="isOpen" @close="onClose" class="relative z-50"> + <!-- The backdrop, rendered as a fixed sibling to the panel container --> + <div class="fixed inset-0 bg-black/30" aria-hidden="true" /> + + <!-- Full-screen container to center the panel --> + <div class="fixed inset-0 flex items-center justify-center w-screen p-4"> + <!-- The actual dialog panel --> + <DialogPanel class="p-2 bg-white rounded dark:bg-dark-700" ref="dialog"> + <DialogDescription> + <img class="preview-image" :src="imgUrl" alt="preview" /> + </DialogDescription> + <!-- ... --> + </DialogPanel> + </div> + </Dialog> + </div> +</template> <style lang="scss"> .preview-image { diff --git a/front-end/src/views/Blog/index.vue b/front-end/src/views/Blog/index.vue index de435ec..af6ab88 100644 --- a/front-end/src/views/Blog/index.vue +++ b/front-end/src/views/Blog/index.vue @@ -1,3 +1,35 @@ +<script setup lang="ts"> +import { computed } from 'vue'; +import { useRouteQuery } from '@vueuse/router'; +import { TabGroup, TabList, Tab, TabPanels, TabPanel, Switch } from '@headlessui/vue' +import { defer, first } from 'lodash-es'; +import { useStore, SortType } from '../../store'; +import Channels from './components/Channels.vue'; +import Posts from './components/Posts.vue'; +import Content from './components/Content.vue'; + +//Protect page +const store = useStore() +store.setPageTitle('Blog Admin') + +const firstLetter = computed(() => first(store.userName)) +const tabIdQ = useRouteQuery<string>('tabid', '', { mode: 'push' }) + +//Map queries to their respective computed values +const tabId = computed(() => tabIdQ.value ? parseInt(tabIdQ.value) : 0); +const lastModified = computed({ + get: () => store.queryState.sort === SortType.ModifiedTime, + set: (value: boolean) => { + store.queryState.sort = value ? SortType.ModifiedTime : SortType.CreatedTime + } +}) + +const onTabChange = (id: number) => tabIdQ.value = id.toString(10) + +//Load channels on page load +defer(() => store.channels.refresh()); + +</script> <template> <div class="container mx-auto mt-10 mb-[10rem]"> <div id="blog-admin-template" class=""> @@ -107,40 +139,6 @@ </div> </template> -<script setup lang="ts"> -import { computed } from 'vue'; -import { useRouteQuery } from '@vueuse/router'; -import { TabGroup, TabList, Tab, TabPanels, TabPanel, Switch } from '@headlessui/vue' -import { defer, first } from 'lodash-es'; -import { useStore, SortType } from '../../store'; -import Channels from './components/Channels.vue'; -import Posts from './components/Posts.vue'; -import Content from './components/Content.vue'; - - -//Protect page -const store = useStore() -store.setPageTitle('Blog Admin') - -const firstLetter = computed(() => first(store.userName)) -const tabIdQ = useRouteQuery<string>('tabid', '', { mode: 'push' }) - -//Map queries to their respective computed values -const tabId = computed(() => tabIdQ.value ? parseInt(tabIdQ.value) : 0); -const lastModified = computed({ - get :() => store.queryState.sort === SortType.ModifiedTime, - set: (value:boolean) => { - store.queryState.sort = value ? SortType.ModifiedTime : SortType.CreatedTime - } -}) - -const onTabChange = (id:number) => tabIdQ.value = id.toString(10) - -//Load channels on page load -defer(() => store.channels.refresh()); - -</script> - <style lang="scss"> #blog-admin-template{ diff --git a/front-end/src/views/Login/components/Social.vue b/front-end/src/views/Login/components/Social.vue index 4d22074..7e92d99 100644 --- a/front-end/src/views/Login/components/Social.vue +++ b/front-end/src/views/Login/components/Social.vue @@ -1,19 +1,3 @@ -<template> - <div class="flex flex-col gap-3"> - <div v-for="method in methods" :key="method.Id" class=""> - <button - type="submit" - class="btn social-button" - :disabled="waiting" - @click.prevent="submitLogin(method)" - > - <fa-icon :icon="getIcon(method)" size="xl" /> - Login with {{ capitalize(method.Id) }} - </button> - </div> - </div> -</template> - <script setup lang="ts"> import { shallowRef } from 'vue' import { apiCall, useWait, type OAuthMethod } from '@vnuge/vnlib.browser' @@ -43,4 +27,22 @@ const getIcon = (method: OAuthMethod): string[] => { //Load methods once the fetch completes store.socialOauth().then(m => methods.value = m.methods); -</script>
\ No newline at end of file +</script> + +<template> + + <div class="flex flex-col gap-3"> + <div v-for="method in methods" :key="method.Id" class=""> + <button + type="submit" + class="btn social-button" + :disabled="waiting" + @click.prevent="submitLogin(method)" + > + <fa-icon :icon="getIcon(method)" size="xl" /> + Login with {{ capitalize(method.Id) }} + </button> + </div> + </div> + +</template> diff --git a/front-end/src/views/Login/components/UserPass.vue b/front-end/src/views/Login/components/UserPass.vue index 442abb1..bc9d8d1 100644 --- a/front-end/src/views/Login/components/UserPass.vue +++ b/front-end/src/views/Login/components/UserPass.vue @@ -55,14 +55,15 @@ </template> <script setup lang="ts"> -import { ref, shallowRef, reactive, defineAsyncComponent, type Ref } from 'vue' +import { ref, shallowRef, reactive, defineAsyncComponent, Ref } from 'vue' import { useTimeoutFn, set } from '@vueuse/core' import { useVuelidate } from '@vuelidate/core' import { isEqual } from 'lodash-es' import { required, maxLength, minLength, email, helpers } from '@vuelidate/validators' import { useVuelidateWrapper, useMfaLogin, totpMfaProcessor, IMfaFlowContinuiation, MfaMethod, - apiCall, useMessage, useWait, debugLog, WebMessage + apiCall, useMessage, useWait, debugLog, WebMessage, + type VuelidateInstance } from '@vnuge/vnlib.browser' const Totp = defineAsyncComponent(() => import('./Totp.vue')) @@ -97,7 +98,7 @@ const rules = { } const v$ = useVuelidate(rules, vState) -const { validate } = useVuelidateWrapper(v$); +const { validate } = useVuelidateWrapper(v$ as Ref<VuelidateInstance>); const SubmitLogin = async () => { diff --git a/front-end/src/views/Login/index.vue b/front-end/src/views/Login/index.vue index 6a55aeb..476ebf4 100644 --- a/front-end/src/views/Login/index.vue +++ b/front-end/src/views/Login/index.vue @@ -1,3 +1,38 @@ +<script setup lang="ts"> +import { computed } from 'vue' +import { apiCall, useWait } from '@vnuge/vnlib.browser' +import { isNil } from 'lodash-es' +import { useStore } from '../../store' +import { storeToRefs } from 'pinia' +import UserPass from './components/UserPass.vue' +import Social from './components/Social.vue' + +const store = useStore(); +const { loggedIn } = storeToRefs(store) +const pkiEnabled = computed(() => !isNil(store.pki?.pkiAuth)) + +store.setPageTitle('Login') + +const { waiting } = useWait() + +const submitLogout = async () => { + //Submit logout request + await apiCall(async ({ toaster }) => { + const { logout } = await store.socialOauth() + // Attempt to logout + await logout() + // Push a new toast message + toaster.general.success({ + id: 'logout-success', + title: 'Success', + text: 'You have been logged out', + duration: 5000 + }) + }) +} + +</script> + <template> <div id="login-template" class="app-component-entry"> <div class="login-container"> @@ -25,7 +60,7 @@ <Social /> <!-- pki button, forward to the pki route --> - <div v-if="pkiEnabled" class="mt-3"> + <div v-if="pkiEnabled" class="mt-4"> <router-link to="/login/pki"> <button type="submit" class="btn red social-button" :disabled="waiting"> <fa-icon :icon="['fa','certificate']" size="xl" /> @@ -39,44 +74,6 @@ </div> </template> -<script setup lang="ts"> -import { } from 'vue' -import { apiCall, useWait } from '@vnuge/vnlib.browser' -import { isNil } from 'lodash-es' -import { useStore } from '../../store' -import { storeToRefs } from 'pinia' -import UserPass from './components/UserPass.vue' -import Social from './components/Social.vue' - -//pki enabled flag from env -const pkiEnabled = !isNil(import.meta.env.VITE_PKI_ENABLED); - -const store = useStore(); -const { loggedIn } = storeToRefs(store) - -store.setPageTitle('Login') - -const { waiting } = useWait() - -const submitLogout = async () => { - //Submit logout request - await apiCall(async ({ toaster }) => { - // Attempt to logout - const { logout } = await store.socialOauth() - await logout() - - // Push a new toast message - toaster.general.success({ - id: 'logout-success', - title: 'Success', - text: 'You have been logged out', - duration: 5000 - }) - }) -} - -</script> - <style lang="scss"> #login-template { .login-container{ diff --git a/front-end/src/views/Login/pki/index.vue b/front-end/src/views/Login/pki/index.vue index 585942a..8edd063 100644 --- a/front-end/src/views/Login/pki/index.vue +++ b/front-end/src/views/Login/pki/index.vue @@ -1,3 +1,38 @@ +<script setup lang="ts"> +import { isEmpty } from 'lodash-es'; +import { apiCall, debugLog, useMessage } from '@vnuge/vnlib.browser'; +import { ref } from 'vue' +import { decodeJwt } from 'jose' +import { useRouter } from 'vue-router'; +import { useStore } from '../../../store'; + +const { setMessage } = useMessage() +const { push } = useRouter() +const store = useStore() + +const otp = ref('') + +const submit = () => { + + apiCall(async () => { + if (isEmpty(otp.value)) { + setMessage('Please enter your OTP') + return + } + + //try to decode the jwt to confirm its form is valid + const jwt = decodeJwt(otp.value) + debugLog(jwt) + + await store.pki!.pkiAuth.login(otp.value) + + //Go back to login page + push({ name: 'Login' }) + }) +} + +</script> + <template> <div id="pki-login-template" class="app-component-entry"> <div class="container max-w-lg mx-auto mt-6 lg:mt-20"> @@ -30,38 +65,3 @@ </div> </div> </template> - -<script setup lang="ts"> -import { isEmpty } from 'lodash-es'; -import { apiCall, debugLog, useMessage } from '@vnuge/vnlib.browser'; -import { ref } from 'vue' -import { decodeJwt } from 'jose' -import { useRouter } from 'vue-router'; -import { useStore } from '../../../store'; - -const { setMessage } = useMessage() -const { push } = useRouter() -const store = useStore() - -const otp = ref('') - -const submit = () =>{ - - apiCall(async () =>{ - if(isEmpty(otp.value)){ - setMessage('Please enter your OTP') - return - } - - //try to decode the jwt to confirm its form is valid - const jwt = decodeJwt(otp.value) - debugLog(jwt) - - await store.pkiAuth.login(otp.value) - - //Go back to login page - push({ name: 'Login' }) - }) -} - -</script>
\ No newline at end of file diff --git a/front-end/src/views/Login/social/[type].vue b/front-end/src/views/Login/social/[type].vue index 51da94f..f011f9c 100644 --- a/front-end/src/views/Login/social/[type].vue +++ b/front-end/src/views/Login/social/[type].vue @@ -1,35 +1,3 @@ -<template> - <div id="social-login-template" class="app-component-entry"> - <div class="container flex flex-col m-auto my-16"> - <div id="social-final-template" class="flex justify-center"> - <div class="entry-container"> - <h3>Finalizing login</h3> - <div class="mt-6 mb-4"> - <div v-if="message?.length > 0" class="text-lg text-red-500 dark:text-rose-500"> - <p>{{ message }}</p> - <div class="flex justify-center mt-5"> - <router-link to="/login"> - <button type="submit" class="btn primary" :disabled="waiting"> - <fa-icon icon="sign-in-alt" /> - Try again - </button> - </router-link> - </div> - </div> - <div v-else> - <div class="flex justify-center"> - <div class="m-auto"> - <fa-icon class="animate-spin" icon="spinner" size="2x"/> - </div> - </div> - <p>Please wait while we log you in.</p> - </div> - </div> - </div> - </div> - </div> - </div> -</template> <script setup lang="ts"> import { defer } from 'lodash-es' @@ -62,19 +30,20 @@ tryOnMounted(() => defer(() => { //try to complete an oauth login apiCall(async ({ toaster }) => { - try{ - //Complete the login - const { completeLogin } = await store.socialOauth(); - await completeLogin() - - toaster.general.success({ - title:'Login Successful', - text: 'You have successfully logged in.' - }) - - router.push({ name: 'Login' }) + try { + const { completeLogin } = await store.socialOauth(); + + //Complete the login + await completeLogin(); + + toaster.general.success({ + title: 'Login Successful', + text: 'You have successfully logged in.' + }) + + router.push({ name: 'Login' }) } - catch(err: any){ + catch (err: any) { set(message, err.message) } }) @@ -82,6 +51,39 @@ tryOnMounted(() => defer(() => { </script> +<template> + <div id="social-login-template" class="app-component-entry"> + <div class="container flex flex-col m-auto my-16"> + <div id="social-final-template" class="flex justify-center"> + <div class="entry-container"> + <h3>Finalizing login</h3> + <div class="mt-6 mb-4"> + <div v-if="message?.length > 0" class="text-lg text-red-500 dark:text-rose-500"> + <p>{{ message }}</p> + <div class="flex justify-center mt-5"> + <router-link to="/login"> + <button type="submit" class="btn primary" :disabled="waiting"> + <fa-icon icon="sign-in-alt" /> + Try again + </button> + </router-link> + </div> + </div> + <div v-else> + <div class="flex justify-center"> + <div class="m-auto"> + <fa-icon class="animate-spin" icon="spinner" size="2x"/> + </div> + </div> + <p>Please wait while we log you in.</p> + </div> + </div> + </div> + </div> + </div> + </div> +</template> + <style lang="scss"> #social-login-template{ |