diff options
author | vnugent <public@vaughnnugent.com> | 2024-01-28 19:56:02 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2024-01-28 19:56:02 -0500 |
commit | 87645bfad3943e1110e4cb2e038124083e8ae793 (patch) | |
tree | c327a4437c98d973f45c313cf8259ad75515c4fe /extension | |
parent | c438ee90e3be4e5e01ae3d045d6b841a03bd46eb (diff) |
progress update
Diffstat (limited to 'extension')
33 files changed, 1376 insertions, 880 deletions
diff --git a/extension/package-lock.json b/extension/package-lock.json index bf558a0..aa48e32 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -17,7 +17,7 @@ "@fortawesome/vue-fontawesome": "^3.0.3", "@headlessui/vue": "^1.7.x", "@kyvg/vue3-notification": "^3.0.x", - "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/0ca26fc63cc5311298575209b124516139f58206/@vnuge-vnlib.browser/release.tgz", + "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/df7dc615532d3441f527374d18664c1a5a336de6/@vnuge-vnlib.browser/release.tgz", "@vuelidate/core": "^2.0.0", "@vuelidate/validators": "^2.0.0", "@vueuse/core": "^10.3.2", @@ -239,9 +239,9 @@ } }, "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" }, @@ -317,9 +317,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" ], @@ -332,9 +332,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" ], @@ -347,9 +347,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" ], @@ -362,9 +362,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" ], @@ -377,9 +377,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" ], @@ -392,9 +392,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" ], @@ -407,9 +407,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" ], @@ -422,9 +422,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" ], @@ -437,9 +437,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" ], @@ -452,9 +452,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" ], @@ -467,9 +467,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" ], @@ -482,9 +482,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" ], @@ -497,9 +497,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" ], @@ -512,9 +512,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" ], @@ -527,9 +527,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" ], @@ -542,9 +542,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" ], @@ -557,9 +557,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" ], @@ -572,9 +572,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" ], @@ -587,9 +587,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" ], @@ -602,9 +602,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" ], @@ -617,9 +617,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" ], @@ -632,9 +632,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" ], @@ -647,9 +647,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" ], @@ -783,9 +783,12 @@ } }, "node_modules/@headlessui/vue": { - "version": "1.7.16", - "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.16.tgz", - "integrity": "sha512-nKT+nf/q6x198SsyK54mSszaQl/z+QxtASmgMEJtpxSX2Q0OPJX0upS/9daDyiECpeAsvjkoOrm2O/6PyBQ+Qg==", + "version": "1.7.17", + "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.17.tgz", + "integrity": "sha512-hmJChv8HzKorxd9F70RGnECAwZfkvmmwOqreuKLWY/19d5qbWnSdw+DNbuA/Uo6X5rb4U5B3NrT+qBKPmjhRqw==", + "dependencies": { + "@tanstack/vue-virtual": "^3.0.0-beta.60" + }, "engines": { "node": ">=10" }, @@ -794,12 +797,12 @@ } }, "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==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "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": { @@ -819,9 +822,9 @@ } }, "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==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -905,9 +908,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==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -923,9 +926,9 @@ } }, "node_modules/@mdn/browser-compat-data": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.4.3.tgz", - "integrity": "sha512-+VnaO5zYUwFQVuRqp2qLPGR5GwhhJ/lrp0yEmamJ/nI15P2GKwGBEWRDiITZR8i6AYxeiQSu2rOi/gqxehnPuA==", + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.5.7.tgz", + "integrity": "sha512-DoHTZ/TjtNfUu9eiqJd+x3IcCQrhS+yOYU436TKUnlE36jZwNbK535D1CmTsSYdi/UcdCWNm5KRQZ9g1tlZCPw==", "dev": true }, "node_modules/@nodelib/fs.scandir": { @@ -1008,9 +1011,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.2.tgz", - "integrity": "sha512-RKzxFxBHq9ysZ83fn8Iduv3A283K7zPPYuhL/z9CQuyFrjwpErJx0h4aeb/bnJ+q29GRLgJpY66ceQ/Wcsn3wA==", + "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" ], @@ -1020,9 +1023,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.2.tgz", - "integrity": "sha512-yZ+MUbnwf3SHNWQKJyWh88ii2HbuHCFQnAYTeeO1Nb8SyEiWASEi5dQUygt3ClHWtA9My9RQAYkjvrsZ0WK8Xg==", + "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" ], @@ -1032,9 +1035,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.2.tgz", - "integrity": "sha512-vqJ/pAUh95FLc/G/3+xPqlSBgilPnauVf2EXOQCZzhZJCXDXt/5A8mH/OzU6iWhb3CNk5hPJrh8pqJUPldN5zw==", + "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" ], @@ -1044,9 +1047,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.2.tgz", - "integrity": "sha512-otPHsN5LlvedOprd3SdfrRNhOahhVBwJpepVKUN58L0RnC29vOAej1vMEaVU6DadnpjivVsNTM5eNt0CcwTahw==", + "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" ], @@ -1056,9 +1059,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.2.tgz", - "integrity": "sha512-ewG5yJSp+zYKBYQLbd1CUA7b1lSfIdo9zJShNTyc2ZP1rcPrqyZcNlsHgs7v1zhgfdS+kW0p5frc0aVqhZCiYQ==", + "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" ], @@ -1068,9 +1071,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.2.tgz", - "integrity": "sha512-pL6QtV26W52aCWTG1IuFV3FMPL1m4wbsRG+qijIvgFO/VBsiXJjDPE/uiMdHBAO6YcpV4KvpKtd0v3WFbaxBtg==", + "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" ], @@ -1080,9 +1083,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.2.tgz", - "integrity": "sha512-On+cc5EpOaTwPSNetHXBuqylDW+765G/oqB9xGmWU3npEhCh8xu0xqHGUA+4xwZLqBbIZNcBlKSIYfkBm6ko7g==", + "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" ], @@ -1092,9 +1095,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.2.tgz", - "integrity": "sha512-Wnx/IVMSZ31D/cO9HSsU46FjrPWHqtdF8+0eyZ1zIB5a6hXaZXghUKpRrC4D5DcRTZOjml2oBhXoqfGYyXKipw==", + "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" ], @@ -1104,9 +1107,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.2.tgz", - "integrity": "sha512-ym5x1cj4mUAMBummxxRkI4pG5Vht1QMsJexwGP8547TZ0sox9fCLDHw9KCH9c1FO5d9GopvkaJsBIOkTKxksdw==", + "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" ], @@ -1116,9 +1119,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.2.tgz", - "integrity": "sha512-m0hYELHGXdYx64D6IDDg/1vOJEaiV8f1G/iO+tejvRCJNSwK4jJ15e38JQy5Q6dGkn1M/9KcyEOwqmlZ2kqaZg==", + "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" ], @@ -1128,9 +1131,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.2.tgz", - "integrity": "sha512-x1CWburlbN5JjG+juenuNa4KdedBdXLjZMp56nHFSHTOsb/MI2DYiGzLtRGHNMyydPGffGId+VgjOMrcltOksA==", + "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" ], @@ -1140,9 +1143,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.2.tgz", - "integrity": "sha512-VVzCB5yXR1QlfsH1Xw1zdzQ4Pxuzv+CPr5qpElpKhVxlxD3CRdfubAG9mJROl6/dmj5gVYDDWk8sC+j9BI9/kQ==", + "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" ], @@ -1152,9 +1155,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.2.tgz", - "integrity": "sha512-SYRedJi+mweatroB+6TTnJYLts0L0bosg531xnQWtklOI6dezEagx4Q0qDyvRdK+qgdA3YZpjjGuPFtxBmddBA==", + "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" ], @@ -1216,10 +1219,34 @@ "node": ">=14.16" } }, + "node_modules/@tanstack/virtual-core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.0.0.tgz", + "integrity": "sha512-SYXOBTjJb05rXa2vl55TTwO40A6wKu0R5i1qQwhJYNDIqaIGF7D0HsLw+pJAyi2OvntlEIVusx3xtbbgSUi6zg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/vue-virtual": { + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.0.0" + } + }, "node_modules/@types/chrome": { - "version": "0.0.254", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.254.tgz", - "integrity": "sha512-svkOGKwA+6ZZuk9xtrYun8MYpNY/9hD17rgZ19v3KunhsK1ZOKaMESw12/1AXLh1u3UPA8jQIRi2370DXv9wgw==", + "version": "0.0.259", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.259.tgz", + "integrity": "sha512-WP1HsLqKgoUwR/4dYiTfmOSUG5B05+xrPLbqboO15nuaUC+aBYxwB9ixVyLPYY9D+vocJK9rzH5g1lpqVrJqhg==", "dev": true, "dependencies": { "@types/filesystem": "*", @@ -1231,6 +1258,11 @@ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, "node_modules/@types/etag": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@types/etag/-/etag-1.8.3.tgz", @@ -1278,9 +1310,9 @@ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==" }, "node_modules/@types/node": { - "version": "20.10.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", - "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "version": "20.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz", + "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", "dependencies": { "undici-types": "~5.26.4" } @@ -1311,9 +1343,9 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, "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" @@ -1325,8 +1357,8 @@ }, "node_modules/@vnuge/vnlib.browser": { "version": "0.1.13", - "resolved": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/0ca26fc63cc5311298575209b124516139f58206/@vnuge-vnlib.browser/release.tgz", - "integrity": "sha512-iivHn8FYtHiLnnID/iUwJlDOluuJeno6OUiqS1z1i93gHRFEKvnTiLHKVZAWzu97GQmA96DMtg2Ni26O/jJLFg==", + "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": { "@vueuse/core": "^10.x", @@ -1387,55 +1419,55 @@ } }, "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==", + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", "dev": true }, "node_modules/@vue/compiler-core": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.3.tgz", - "integrity": "sha512-u8jzgFg0EDtSrb/hG53Wwh1bAOQFtc1ZCegBpA/glyvTlgHl+tq13o1zvRfLbegYUw/E4mSTGOiCnAJ9SJ+lsg==", + "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.3", + "@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.3", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.3.tgz", - "integrity": "sha512-oGF1E9/htI6JWj/lTJgr6UgxNCtNHbM6xKVreBWeZL9QhRGABRVoWGAzxmtBfSOd+w0Zi5BY0Es/tlJrN6WgEg==", + "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.3", - "@vue/shared": "3.4.3" + "@vue/compiler-core": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.3.tgz", - "integrity": "sha512-NuJqb5is9I4uzv316VRUDYgIlPZCG8D+ARt5P4t5UDShIHKL25J3TGZAUryY/Aiy0DsY7srJnZL5ryB6DD63Zw==", + "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.3", - "@vue/compiler-dom": "3.4.3", - "@vue/compiler-ssr": "3.4.3", - "@vue/shared": "3.4.3", + "@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.3", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.3.tgz", - "integrity": "sha512-wnYQtMBkeFSxgSSQbYGQeXPhQacQiog2c6AlvMldQH6DB+gSXK/0F6DVXAJfEiuBSgBhUc8dwrrG5JQcqwalsA==", + "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.3", - "@vue/shared": "3.4.3" + "@vue/compiler-dom": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/devtools-api": { @@ -1493,48 +1525,48 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.3.tgz", - "integrity": "sha512-q5f9HLDU+5aBKizXHAx0w4whkIANs1Muiq9R5YXm0HtorSlflqv9u/ohaMxuuhHWCji4xqpQ1eL04WvmAmGnFg==", + "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.3" + "@vue/shared": "3.4.15" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.3.tgz", - "integrity": "sha512-C1r6QhB1qY7D591RCSFhMULyzL9CuyrGc+3PpB0h7dU4Qqw6GNyo4BNFjHZVvsWncrUlKX3DIKg0Y7rNNr06NQ==", + "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.3", - "@vue/shared": "3.4.3" + "@vue/reactivity": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.3.tgz", - "integrity": "sha512-wrsprg7An5Ec+EhPngWdPuzkp0BEUxAKaQtN9dPU/iZctPyD9aaXmVtehPJerdQxQale6gEnhpnfywNw3zOv2A==", + "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.3", - "@vue/shared": "3.4.3", + "@vue/runtime-core": "3.4.15", + "@vue/shared": "3.4.15", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.3.tgz", - "integrity": "sha512-BUxt8oVGMKKsqSkM1uU3d3Houyfy4WAc2SpSQRebNd+XJGATVkW/rO129jkyL+kpB/2VRKzE63zwf5RtJ3XuZw==", + "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.3", - "@vue/shared": "3.4.3" + "@vue/compiler-ssr": "3.4.15", + "@vue/shared": "3.4.15" }, "peerDependencies": { - "vue": "3.4.3" + "vue": "3.4.15" } }, "node_modules/@vue/shared": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.3.tgz", - "integrity": "sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ==" + "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", @@ -1621,13 +1653,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": { @@ -1660,12 +1692,12 @@ } }, "node_modules/@vueuse/integrations": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.7.1.tgz", - "integrity": "sha512-cKo5LEeKVHdBRBtMTOrDPdR0YNtrmN9IBfdcnY2P3m5LHVrsD0xiHUtAH1WKjHQRIErZG6rJUa6GA4tWZt89Og==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.7.2.tgz", + "integrity": "sha512-+u3RLPFedjASs5EKPc69Ge49WNgqeMfSxFn+qrQTzblPXZg6+EFzhjarS5edj2qAf6xQ93f95TUxRwKStXj/sQ==", "dependencies": { - "@vueuse/core": "10.7.1", - "@vueuse/shared": "10.7.1", + "@vueuse/core": "10.7.2", + "@vueuse/shared": "10.7.2", "vue-demi": ">=0.14.6" }, "funding": { @@ -1750,17 +1782,17 @@ } }, "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/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" }, @@ -1825,35 +1857,35 @@ } }, "node_modules/addons-linter": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-6.19.0.tgz", - "integrity": "sha512-Yz5YRfIQ12dIqXKmEoHNwoS/L1xIve2hdm9UEesOEsov5W0zeWMOWCnqdvJNVNy7f1FnPu1lnqzylelkTBS7dA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-6.21.0.tgz", + "integrity": "sha512-4GBn14BR16FZE7dog6uz+1HO6V3B+mAVxmbwxRhed2y5eyrwIW832TmEpku+5A5bbovBZ4gilXEtBsl6A1AMmg==", "dev": true, "dependencies": { "@fluent/syntax": "0.19.0", - "@mdn/browser-compat-data": "5.4.3", + "@mdn/browser-compat-data": "5.5.7", "addons-moz-compare": "1.3.0", - "addons-scanner-utils": "9.8.0", + "addons-scanner-utils": "9.9.0", "ajv": "8.12.0", "chalk": "4.1.2", "cheerio": "1.0.0-rc.12", "columnify": "1.6.0", "common-tags": "1.8.2", "deepmerge": "4.3.1", - "eslint": "8.55.0", + "eslint": "8.56.0", "eslint-plugin-no-unsanitized": "4.0.2", "eslint-visitor-keys": "3.4.3", "espree": "9.6.1", "esprima": "4.0.1", "fast-json-patch": "3.1.1", "glob": "10.3.10", - "image-size": "1.0.2", + "image-size": "1.1.1", "is-mergeable-object": "1.1.1", "jed": "1.1.1", "json-merge-patch": "1.0.2", "os-locale": "5.0.0", - "pino": "8.16.2", - "postcss": "8.4.32", + "pino": "8.17.2", + "postcss": "8.4.33", "relaxed-json": "1.0.3", "semver": "7.5.4", "sha.js": "2.4.11", @@ -1870,19 +1902,10 @@ "node": ">=16.0.0" } }, - "node_modules/addons-linter/node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/addons-linter/node_modules/addons-scanner-utils": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-9.8.0.tgz", - "integrity": "sha512-nJJ4QazrtMImyb2OK9SGZlNtinNu25dzOR0lhWthhJQN2iDOf3yqHdSiVBEeZvCwuT/sS1cU6me4O4kgEATjFQ==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-9.9.0.tgz", + "integrity": "sha512-YDP10U3sEZMuIgnjXMiAYgUU64jTbxmhpUXMlhi1nKO4Etz+ctGWoTUst7IQRoLWaY9y2r1KZDG3jALxLA1n7Q==", "dev": true, "dependencies": { "@types/yauzl": "2.10.3", @@ -1958,83 +1981,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/addons-linter/node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/addons-linter/node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/addons-linter/node_modules/eslint/node_modules/json-schema-traverse": { - "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 - }, "node_modules/addons-linter/node_modules/glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -2057,7 +2003,13 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/addons-linter/node_modules/glob/node_modules/minimatch": { + "node_modules/addons-linter/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/addons-linter/node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", @@ -2072,12 +2024,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/addons-linter/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/addons-linter/node_modules/node-fetch": { "version": "2.6.11", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", @@ -2346,9 +2292,9 @@ } }, "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": [ { @@ -2365,9 +2311,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" @@ -2398,11 +2344,11 @@ "dev": true }, "node_modules/axios": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz", - "integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==", + "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.0", + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -2536,9 +2482,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": [ { @@ -2555,8 +2501,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" }, @@ -2716,9 +2662,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001572", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz", - "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==", + "version": "1.0.30001581", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz", + "integrity": "sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ==", "dev": true, "funding": [ { @@ -3486,9 +3432,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" @@ -3535,9 +3481,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.616", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz", - "integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==", + "version": "1.4.648", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.648.tgz", + "integrity": "sha512-EmFMarXeqJp9cUKu/QEciEApn0S/xRcpZWuAm32U7NgoZCimjsilKXHRO9saeEW55eHZagIDg6XTUOv32w9pjg==", "dev": true }, "node_modules/emoji-regex": { @@ -3594,9 +3540,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==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -3605,29 +3551,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": { @@ -3725,9 +3671,9 @@ } }, "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==", + "version": "9.20.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.20.1.tgz", + "integrity": "sha512-GyCs8K3lkEvoyC1VV97GJhP1SvqsKCiWGHnbn0gVUYiUhaH2+nB+Dv1uekv1THFMPbBfYxukrzQdltw950k+LQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -3735,7 +3681,7 @@ "nth-check": "^2.1.1", "postcss-selector-parser": "^6.0.13", "semver": "^7.5.4", - "vue-eslint-parser": "^9.3.1", + "vue-eslint-parser": "^9.4.0", "xml-name-validator": "^4.0.0" }, "engines": { @@ -3967,9 +3913,9 @@ } }, "node_modules/fastq": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", - "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "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" } @@ -4112,9 +4058,9 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" }, "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", @@ -4217,7 +4163,6 @@ "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -4255,9 +4200,9 @@ } }, "node_modules/fx-runner": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fx-runner/-/fx-runner-1.3.0.tgz", - "integrity": "sha512-5b37H4GCyhF+Nf8xk9mylXoDq4wb7pbGAXxlCXp/631UTeeZomWSYcEGXumY4wk8g2QAqjPMGdWW+RbNt8PUcA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/fx-runner/-/fx-runner-1.4.0.tgz", + "integrity": "sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==", "dependencies": { "commander": "2.9.0", "shell-quote": "1.7.3", @@ -4682,9 +4627,9 @@ } }, "node_modules/image-size": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz", - "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", "dev": true, "dependencies": { "queue": "6.0.2" @@ -4693,7 +4638,7 @@ "image-size": "bin/image-size.js" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.x" } }, "node_modules/immediate": { @@ -4702,9 +4647,9 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, "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==", "devOptional": true }, "node_modules/import-fresh": { @@ -5943,9 +5888,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" }, @@ -6154,9 +6099,9 @@ } }, "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" @@ -6251,9 +6196,9 @@ } }, "node_modules/pino": { - "version": "8.16.2", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.2.tgz", - "integrity": "sha512-2advCDGVEvkKu9TTVSa/kWW7Z3htI/sBKEZpqiHk6ive0i/7f5b1rsU8jn0aimxqfnSz5bj/nOYkwhBUn5xxvg==", + "version": "8.17.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", + "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0", @@ -6261,7 +6206,7 @@ "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "v1.1.0", "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0", + "process-warning": "^3.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", @@ -6307,9 +6252,9 @@ } }, "node_modules/postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "funding": [ { "type": "opencollective", @@ -6506,9 +6451,9 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "node_modules/process-warning": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", - "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", "dev": true }, "node_modules/promise-toolbox": { @@ -7002,9 +6947,12 @@ } }, "node_modules/rollup": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.2.tgz", - "integrity": "sha512-66RB8OtFKUTozmVEh3qyNfH+b+z2RXBVloqO2KCC/pjFaGaHtxP9fVfOQKPSGXg2mElmjmxjW/fZ7iKrEpMH5Q==", + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz", + "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==", + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, @@ -7013,19 +6961,19 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.9.2", - "@rollup/rollup-android-arm64": "4.9.2", - "@rollup/rollup-darwin-arm64": "4.9.2", - "@rollup/rollup-darwin-x64": "4.9.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.2", - "@rollup/rollup-linux-arm64-gnu": "4.9.2", - "@rollup/rollup-linux-arm64-musl": "4.9.2", - "@rollup/rollup-linux-riscv64-gnu": "4.9.2", - "@rollup/rollup-linux-x64-gnu": "4.9.2", - "@rollup/rollup-linux-x64-musl": "4.9.2", - "@rollup/rollup-win32-arm64-msvc": "4.9.2", - "@rollup/rollup-win32-ia32-msvc": "4.9.2", - "@rollup/rollup-win32-x64-msvc": "4.9.2", + "@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" } }, @@ -7093,9 +7041,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.69.6", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.6.tgz", - "integrity": "sha512-qbRr3k9JGHWXCvZU77SD2OTwUlC+gNT+61JOLcmLm+XqH4h/5D+p4IIsxvpkB89S9AwJOyb5+rWNpIucaFxSFQ==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", + "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", "devOptional": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -7174,15 +7122,16 @@ "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==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", "dev": true, "dependencies": { "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -7239,6 +7188,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/sign-addon/-/sign-addon-5.3.0.tgz", "integrity": "sha512-7nHlCzhQgVMLBNiXVEgbG/raq48awOW0lYMN5uo1BaB3mp0+k8M8pvDwbfTlr3apcxZJsk9HQsAW1POwoJugpQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "dependencies": { "common-tags": "1.8.2", @@ -7259,9 +7209,9 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sonic-boom": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz", - "integrity": "sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz", + "integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0" @@ -7619,9 +7569,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", - "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -8017,9 +7967,9 @@ } }, "node_modules/universal-cookie": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.0.0.tgz", - "integrity": "sha512-T3XwZ2cUbHRU+UNfPSaPd0zti50tVIvk6onLA90pa+qKwsP8ksn5pwYM7rWMODoX1OCA9qPAN8uK88Avq5YbtQ==", + "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" @@ -8147,9 +8097,9 @@ } }, "node_modules/vite": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz", - "integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", "dependencies": { "esbuild": "^0.19.3", "postcss": "^8.4.32", @@ -8289,12 +8239,12 @@ } }, "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==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.1.2.tgz", + "integrity": "sha512-wkWfEx/IIR3s2P5yD4aTGHiOb8IAzFxgkSt1uSC3itJ4oDAm23yG7o0L29JljUdnXDDgLafPAvhv8A2I/8riHw==", "dev": true, "dependencies": { - "@vscode/l10n": "^0.0.16", + "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.11", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.0.8" @@ -8319,15 +8269,15 @@ "dev": true }, "node_modules/vue": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.3.tgz", - "integrity": "sha512-GjN+culMAGv/mUbkIv8zMKItno8npcj5gWlXkSxf1SPTQf8eJ4A+YfHIvQFyL1IfuJcMl3soA7SmN1fRxbf/wA==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz", + "integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==", "dependencies": { - "@vue/compiler-dom": "3.4.3", - "@vue/compiler-sfc": "3.4.3", - "@vue/runtime-dom": "3.4.3", - "@vue/server-renderer": "3.4.3", - "@vue/shared": "3.4.3" + "@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": "*" @@ -8339,9 +8289,9 @@ } }, "node_modules/vue-eslint-parser": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz", - "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==", + "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", @@ -8437,14 +8387,14 @@ } }, "node_modules/web-ext": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.9.0.tgz", - "integrity": "sha512-oWMiM3e+u5E8X7aUMgQ0BCGjlbZt4XwF6ExAXsXx9Btdz3nLmUY/4eKEZA1J+2T7WhCdRwN7Pdh2VKMej/pthQ==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.11.0.tgz", + "integrity": "sha512-EG6YXHITNDJB/h6Rc5FF08eMoN45sZPBBIIlEraBzxJ0RdJZ8Z3xvUUawbDwt+mowfv9X0XRWlLSwdWbRKgojg==", "dev": true, "dependencies": { "@babel/runtime": "7.21.0", "@devicefarmer/adbkit": "3.2.3", - "addons-linter": "6.19.0", + "addons-linter": "6.21.0", "bunyan": "1.8.15", "camelcase": "7.0.1", "chrome-launcher": "0.15.1", @@ -8453,7 +8403,7 @@ "es6-error": "4.1.1", "firefox-profile": "4.3.2", "fs-extra": "11.1.0", - "fx-runner": "1.3.0", + "fx-runner": "1.4.0", "import-fresh": "3.3.0", "jose": "4.13.1", "mkdirp": "1.0.4", @@ -8484,19 +8434,19 @@ } }, "node_modules/web-ext-run": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/web-ext-run/-/web-ext-run-0.1.1.tgz", - "integrity": "sha512-9QGtlnXExhXYK+pRFaN/ZRbS4jb53YwQn7A1zOlAE4x5Lj6V7l1PD7O/wbHaGe7xkykrw7+Cv4i0ukSi7VBnsg==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/web-ext-run/-/web-ext-run-0.1.2.tgz", + "integrity": "sha512-VygO7lg10keonbku5Lbi21zEz1k91odkW+c6YB5DGb+mITcRMVM2zr/tJ+Suh6wxNUOOFNbinoqDlSUWdxt6YQ==", "dependencies": { - "@babel/runtime": "7.23.4", - "@devicefarmer/adbkit": "3.2.5", + "@babel/runtime": "7.23.9", + "@devicefarmer/adbkit": "3.2.6", "bunyan": "1.8.15", "chrome-launcher": "0.15.1", "debounce": "1.2.1", "es6-error": "4.1.1", - "firefox-profile": "4.4.0", - "fs-extra": "11.1.1", - "fx-runner": "1.3.0", + "firefox-profile": "4.5.0", + "fs-extra": "11.2.0", + "fx-runner": "1.4.0", "mkdirp": "1.0.4", "multimatch": "6.0.0", "mz": "2.7.0", @@ -8509,18 +8459,18 @@ "tmp": "0.2.1", "update-notifier": "6.0.2", "watchpack": "2.4.0", - "ws": "8.14.2", + "ws": "8.16.0", "zip-dir": "2.0.0" }, "engines": { - "node": ">=16.0.0", + "node": ">=18.0.0", "npm": ">=8.0.0" } }, "node_modules/web-ext-run/node_modules/@babel/runtime": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.4.tgz", - "integrity": "sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -8529,9 +8479,9 @@ } }, "node_modules/web-ext-run/node_modules/@devicefarmer/adbkit": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit/-/adbkit-3.2.5.tgz", - "integrity": "sha512-+J479WWZW3GU3t40flicDfiDrFz6vpiy2RcBQPEhFcs/3La9pOtr4Bgz2Q02E4luUG2RAL068rqIkKNUTy3tZw==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit/-/adbkit-3.2.6.tgz", + "integrity": "sha512-8lO1hSeTgtxcOHhp4tTWq/JaOysp5KNbbyFoxNEBnwkCDZu/Bji3ZfOaG++Riv9jN6c9bgdLBOZqJTC5VJPRKQ==", "dependencies": { "@devicefarmer/adbkit-logcat": "^2.1.2", "@devicefarmer/adbkit-monkey": "~1.2.1", @@ -8557,9 +8507,9 @@ } }, "node_modules/web-ext-run/node_modules/firefox-profile": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/firefox-profile/-/firefox-profile-4.4.0.tgz", - "integrity": "sha512-n+Wwa7yNtgJcOOWJXVOpF84rlDdPGsZ5d2dAuCZJivf3SSmx2omWFRDU85l4T5HZ3jj+FJQbyto04pX7/HWPnA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/firefox-profile/-/firefox-profile-4.5.0.tgz", + "integrity": "sha512-goE2XxbmYVSafvCjcy64/AK3xOr14HCUCD4+TpYWEIMy4nrJfNAacLGzwqKwZhCW0hHI2TYMGH+G/YBvOE8L6g==", "dependencies": { "adm-zip": "~0.5.x", "fs-extra": "~9.0.1", @@ -8585,27 +8535,6 @@ "node": ">=10" } }, - "node_modules/web-ext-run/node_modules/firefox-profile/node_modules/universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/web-ext-run/node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/web-ext-run/node_modules/json-parse-even-better-errors": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", @@ -8678,10 +8607,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/web-ext-run/node_modules/universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/web-ext-run/node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "engines": { "node": ">=10.0.0" }, @@ -8833,9 +8770,9 @@ } }, "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", + "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", "dev": true, "engines": { "node": ">= 8" diff --git a/extension/package.json b/extension/package.json index 806d54f..ba5d172 100644 --- a/extension/package.json +++ b/extension/package.json @@ -44,7 +44,7 @@ "@fortawesome/vue-fontawesome": "^3.0.3", "@headlessui/vue": "^1.7.x", "@kyvg/vue3-notification": "^3.0.x", - "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/0ca26fc63cc5311298575209b124516139f58206/@vnuge-vnlib.browser/release.tgz", + "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/df7dc615532d3441f527374d18664c1a5a336de6/@vnuge-vnlib.browser/release.tgz", "@vuelidate/core": "^2.0.0", "@vuelidate/validators": "^2.0.0", "@vueuse/core": "^10.3.2", diff --git a/extension/src/components/ListBox.vue b/extension/src/components/ListBox.vue new file mode 100644 index 0000000..704cc58 --- /dev/null +++ b/extension/src/components/ListBox.vue @@ -0,0 +1,86 @@ +<template> + <Listbox as="div" class="relative w-full"> + + <ListboxButton class="inline-flex items-center w-full overflow-hidden bg-white border rounded-md dark:bg-dark-800 dark:border-dark-500"> + <span class="flex-1 px-4 py-2 text-gray-600 dark:text-inherit border-e dark:border-dark-500 text-sm/none hover:bg-gray-50 hover:text-gray-700 dark:hover:bg-dark-700 dark:hover:text-gray-100" > + {{ $props.modelToString(model) }} + </span> + <span class="h-full p-2 hover:bg-gray-50 hover:text-gray-700 dark:hover:bg-dark-700 dark:hover:text-gray-100"> + <span class="sr-only">Menu</span> + <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor"> + <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> + </svg> + </span> + </ListboxButton> + + <transition + leave-active-class="transition duration-100 ease-in" + leave-from-class="opacity-100" + leave-to-class="opacity-0" + > + <ListboxOptions + role="menu" + class="absolute z-10 w-full mt-1 bg-white border border-gray-100 divide-y divide-gray-100 rounded-md shadow-lg end-0 dark:divide-dark-600 dark:border-dark-500 dark:bg-dark-800" + > + <div + v-for="group in $props.groups" + :key="group.name" + class="" + > + <strong class="block p-2 text-xs font-medium text-gray-400 uppercase dark:text-gray-400"> + {{ group.name }} + </strong> + + <ListboxOption + v-slot="{ active, selected }" + v-for="option in group.options" + :key="option.name" + :value="option.value" + as="template" + @click.prevent="select(option.value)" + > + <li + class='relative px-6 py-1 text-sm duration-75 ease-linear cursor-default select-none' + :class="[active ? 'bg-blue-300 text-blue-900' : 'text-gray-900 dark:text-gray-100']" + > + <span class="block truncate" :class="[selected ? 'font-medium' : 'font-normal']"> + {{ option.name }} + </span> + + <span v-if="selected" class="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600"> + <CheckIcon class="w-5 h-5" aria-hidden="true" /> + </span> + </li> + </ListboxOption> + </div> + </ListboxOptions> + </transition> + + </Listbox> +</template> + +<script setup lang="ts"> +import { Listbox, ListboxButton, ListboxOptions, ListboxOption } from '@headlessui/vue' + +const model = defineModel<any>({ required: true }) +defineProps<ListboxProps<any>>(); + +const select = (value: any) => model.value = value + +export interface Option<T>{ + readonly name: string + readonly value: T +} + +export interface OptionGroup<T>{ + readonly name: string + readonly options: Option<T>[] +} + +export interface ListboxProps<T>{ + readonly modelValue: T; + readonly groups: OptionGroup<T>[]; + modelToString(value:T): string; +} + +</script>
\ No newline at end of file diff --git a/extension/src/components/Pagination.vue b/extension/src/components/Pagination.vue new file mode 100644 index 0000000..9a017ed --- /dev/null +++ b/extension/src/components/Pagination.vue @@ -0,0 +1,36 @@ +<template> + <div class="inline-flex items-center justify-center gap-3"> + <a + href="#" + @click.prevent="$props.pages.prev()" + class="inline-flex items-center justify-center w-8 h-8 text-gray-900 bg-white border border-gray-100 rounded rtl:rotate-180 dark:border-gray-800 dark:bg-gray-900 dark:text-white" + > + <span class="sr-only">Next Page</span> + <fa-icon icon="chevron-left" class="w-4" /> + </a> + + <p class="text-xs text-gray-900 dark:text-white"> + {{ $props.pages.currentPage }} + <span class="mx-0.25">/</span> + {{ $props.pages.pageCount }} + </p> + + <a + href="#" + @click.prevent="$props.pages.next()" + class="inline-flex items-center justify-center w-8 h-8 text-gray-900 bg-white border border-gray-100 rounded rtl:rotate-180 dark:border-gray-800 dark:bg-gray-900 dark:text-white" + > + <span class="sr-only">Next Page</span> + <fa-icon icon="chevron-right" class="w-4" /> + </a> + </div> +</template> + +<script setup lang="ts"> +import { useOffsetPagination } from '@vueuse/core'; + +defineProps<{ + pages: ReturnType<typeof useOffsetPagination> +}>() + +</script>
\ No newline at end of file diff --git a/extension/src/entries/contentScript/primary/components/PromptPopup.vue b/extension/src/entries/contentScript/primary/components/PromptPopup.vue index 1f62877..b2415b9 100644 --- a/extension/src/entries/contentScript/primary/components/PromptPopup.vue +++ b/extension/src/entries/contentScript/primary/components/PromptPopup.vue @@ -1,11 +1,12 @@ <template> - <div v-show="event" id="nvault-ext-prompt" :class="{'dark': darkMode }"> + <div v-show="showPrompt" id="nvault-ext-prompt" :class="{'dark': darkMode }"> <div class="fixed top-0 bottom-0 left-0 right-0 text-white" style="z-index:9147483647 !important" > - + + <!-- Only show backdrop when prompt is on the same origin --> <div class="fixed inset-0 left-0 w-full h-full bg-black/50" @click.self="close" /> - <div v-if="store.permissions.isPopup" class="relative w-full md:max-w-[28rem] mx-auto md:mt-36 mb-auto" ref="prompt"> + <div v-if="showPopup" class="relative w-full md:max-w-[28rem] mx-auto md:mt-36 mb-auto" ref="prompt"> <div class="w-full h-screen p-5 text-gray-800 bg-white border shadow-lg md:h-auto md:rounded dark:bg-dark-900 dark:border-dark-500 dark:text-gray-200"> <div v-if="loggedIn" class=""> @@ -49,16 +50,14 @@ </div> <div class="flex gap-2 mt-4"> + + <ListBox class="max-w-40" v-model="allowRuleType" :groups="lbOptions" :modelToString="modelToString" /> + <div class="ml-auto"> <button class="rounded btn sm" @click="close">Close</button> </div> <div> - <button :disabled="selectedKey?.Id == undefined" class="rounded amber btn sm" @click="allow(true)"> - Always Allow - </button> - </div> - <div> - <button :disabled="selectedKey?.Id == undefined" class="rounded btn sm" @click="allow(false)">Allow</button> + <button :disabled="selectedKey?.Id == undefined" class="rounded btn sm" @click="allow()">Allow</button> </div> </div> </div> @@ -89,13 +88,14 @@ </template> <script setup lang="ts"> -import { ref } from 'vue' +import { ref, shallowRef } from 'vue' import { storeToRefs } from 'pinia'; import { computed } from 'vue'; import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue' -import { clone, first } from 'lodash'; +import { clone, first, isEqual } from 'lodash'; import { useStore } from '../../../store'; -import { type PermissionRequest } from '../../../../features' +import { CreateRuleType, type PermissionRequest } from '../../../../features' +import ListBox, { Option, OptionGroup } from '../../../../components/ListBox.vue'; interface PropmtMessage extends PermissionRequest{ msg: string; @@ -106,6 +106,7 @@ const { loggedIn, selectedKey, darkMode } = storeToRefs(store) const keyName = computed(() => selectedKey.value?.UserName) const prompt = ref(null) +const allowRuleType = shallowRef<CreateRuleType>(CreateRuleType.AllowOnce) const event = computed<PropmtMessage | undefined>(() => { //Use a the current windowpending if set @@ -120,16 +121,26 @@ const event = computed<PropmtMessage | undefined>(() => { const site = computed(() => new URL(event.value?.origin || "https://example.com").host) const evData = computed(() => JSON.stringify(event.value || {}, null, 2)) +const onSameOrigin = computed(() => isEqual(event.value?.origin, window.location.origin)) +//Only show in-page popup if the event is from the same origin +const showPopup = computed(() => (store.permissions.isPopup || !store.settings.authPopup)) +const showPrompt = computed(() => (onSameOrigin.value || store.permissions.isPopup) && event.value) + const close = () => { if(event.value){ store.plugins.permission.deny(event.value.uuid); } + + //Reset the rule type + allowRuleType.value = CreateRuleType.AllowOnce } -const allow = (addRule: boolean) => { +const allow = () => { if (event.value) { - store.plugins.permission.allow(event.value.uuid, addRule); + store.plugins.permission.allow(event.value.uuid, allowRuleType.value); } + //Reset the rule type + allowRuleType.value = CreateRuleType.AllowOnce } //Listen for events @@ -161,4 +172,51 @@ const getPromptMessage = (perms: PermissionRequest | undefined): PropmtMessage | return ev } -</script> +const getRuleName = (rule: CreateRuleType) => { + switch (rule) { + default: + return 'None' + case CreateRuleType.AllowOnce: + return "Allow Once" + case CreateRuleType.AllowForever: + return "Allow Forever" + case CreateRuleType.FiveMinutes: + return "5 Minutes" + case CreateRuleType.OneHour: + return "1 Hour" + case CreateRuleType.OneDay: + return "1 Day" + case CreateRuleType.OneWeek: + return "1 Week" + case CreateRuleType.OneMonth: + return "1 Month" + } +} + +const createOption = (rule: CreateRuleType): Option<CreateRuleType> => { + return { name: getRuleName(rule), value: rule } +} + +const creatGroup = (name: string, options: Option<CreateRuleType>[]): OptionGroup<CreateRuleType> => { + return { name, options } +} + +const lbOptions = ((): OptionGroup<CreateRuleType>[] => { + return[ + creatGroup('Allow', [ + createOption(CreateRuleType.AllowOnce), + createOption(CreateRuleType.AllowForever) + ]), + creatGroup('Allow for', [ + createOption(CreateRuleType.FiveMinutes), + createOption(CreateRuleType.OneHour), + createOption(CreateRuleType.OneDay), + createOption(CreateRuleType.OneWeek), + createOption(CreateRuleType.OneMonth) + ]) + ] +})() + +const modelToString = (rule: CreateRuleType) => getRuleName(rule) + +</script>
\ No newline at end of file diff --git a/extension/src/entries/contentScript/primary/main.js b/extension/src/entries/contentScript/primary/main.js index 15fc2ec..2e05ca3 100644 --- a/extension/src/entries/contentScript/primary/main.js +++ b/extension/src/entries/contentScript/primary/main.js @@ -19,6 +19,7 @@ import { defer } from "lodash"; import { createPinia } from 'pinia'; import { useBackgroundPiniaPlugin, identityPlugin, originPlugin, permissionsPlugin } from '../../store' import { onLoad } from "../util"; +import ListBox from '../../../components/ListBox.vue' import renderContent from "../renderContent"; import App from "./App.vue"; import Notification from '@kyvg/vue3-notification' @@ -64,6 +65,7 @@ renderContent([], (appRoot, shadowRoot) => { .use(store) .use(Notification) .component('fa-icon', FontAwesomeIcon) + .component('list-box', ListBox) .mount(appRoot); //Load the nostr shim diff --git a/extension/src/entries/contentScript/util.ts b/extension/src/entries/contentScript/util.ts index aecb7b2..192f1c1 100644 --- a/extension/src/entries/contentScript/util.ts +++ b/extension/src/entries/contentScript/util.ts @@ -25,7 +25,7 @@ const registerWindowHandler = (store: Store, extName: string) => { const { selectedKey } = storeToRefs(store) const { nostr, permission } = store.plugins; - const onAsyncCall = async ({ source, data, origin } : MessageEvent<any>) => { + const onAsyncCall = async ({ data, origin } : MessageEvent<any>) => { //clean any junk/methods with json parse/stringify data = JSON.parse(JSON.stringify(data)) @@ -38,7 +38,7 @@ const registerWindowHandler = (store: Store, extName: string) => { } //Confirm the message format is correct - if (!isEqual(source, window) || isEmpty(data) || isNil(data.type)) { + if (isEmpty(data) || isNil(data.type)) { return } //Confirm extension is for us diff --git a/extension/src/entries/options/App.vue b/extension/src/entries/options/App.vue index 3dbe94d..f62795f 100644 --- a/extension/src/entries/options/App.vue +++ b/extension/src/entries/options/App.vue @@ -12,7 +12,7 @@ <div class=""> <h2>NVault</h2> </div> - <TabGroup :selected-index="selectedTab" @change="id => selectedTab = id" > + <TabGroup :selected-index="selectedTab" @change="selectTab" > <TabList class="flex gap-3 pb-2 border-b border-gray-300 dark:border-dark-500"> <Tab v-slot="{ selected }"> <button class="tab-title" :class="{ selected }"> @@ -72,7 +72,7 @@ <TabPanel> <Account/> </TabPanel> - <TabPanel> <EventHistory/> </TabPanel> + <TabPanel> <Activity/> </TabPanel> <TabPanel> <Privacy/> </TabPanel> @@ -116,7 +116,7 @@ </template> <script setup lang="ts"> -import { ref, watchEffect } from "vue"; +import { computed, ref, watchEffect } from "vue"; import { TabGroup, TabList, @@ -126,26 +126,32 @@ import { } from '@headlessui/vue' import { apiCall, configureNotifier } from '@vnuge/vnlib.browser'; import { storeToRefs } from "pinia"; -import { type NostrPubKey } from '../../features/'; +import { useQuery, type NostrPubKey } from '../../features/'; import { notify } from "@kyvg/vue3-notification"; -import SiteSettings from './components/SiteSettings.vue'; -import Identities from './components/Identities.vue'; -import Privacy from "./components/Privacy.vue"; +import { toSafeInteger } from "lodash"; import { useStore } from "../store"; import Account from "./components/Account.vue"; import ConfirmPrompt from "../../components/ConfirmPrompt.vue"; import PasswordPrompt from "../../components/PasswordPrompt.vue"; -import EventHistory from "./components/EventHistory.vue"; +import Activity from "./components/Activity.vue"; +import SiteSettings from './components/SiteSettings.vue'; +import Identities from './components/Identities.vue'; +import Privacy from "./components/Privacy.vue"; //Configure the notifier to use the notification library configureNotifier({ notify, close: notify.close }) const store = useStore() +const { identity } = store.plugins const { allKeys, darkMode, userName } = storeToRefs(store) -const selectedTab = ref(0) -const keyBuffer = ref<NostrPubKey>({} as NostrPubKey) +const keyBuffer = ref<Partial<NostrPubKey>>({} as NostrPubKey) + +const tabIdQuery = useQuery('t') +const selectedTab = computed(() => toSafeInteger(tabIdQuery.asRef.value)); +const selectTab = (id: number) => tabIdQuery.set(id.toString()) + const editKey = (key: NostrPubKey) =>{ //Goto hidden tab @@ -158,14 +164,14 @@ const doneEditing = () =>{ //Goto hidden tab selectedTab.value = 0 //Set selected key - keyBuffer.value = null + keyBuffer.value = {} } const onUpdate = async () =>{ await apiCall(async ({ toaster }) => { //Update identity - await store.updateIdentity(keyBuffer.value) + await identity.updateIdentity(keyBuffer.value) //Show success toaster.general.success({ 'title':'Success', @@ -176,7 +182,7 @@ const onUpdate = async () =>{ //Goto hidden tab selectedTab.value = 0 //Set selected key - keyBuffer.value = null + keyBuffer.value = {} } const toggleDark = () => store.toggleDarkMode() diff --git a/extension/src/entries/options/components/Activity.vue b/extension/src/entries/options/components/Activity.vue new file mode 100644 index 0000000..c62fb83 --- /dev/null +++ b/extension/src/entries/options/components/Activity.vue @@ -0,0 +1,121 @@ +<template> + <div id="ev-history" class="flex flex-col w-full mt-4 sm:px-2"> + <form @submit.prevent=""> + <div class="w-full max-w-xl mx-auto"> + <h3 class="text-center"> + Permissions + </h3> + + <div class="flex flex-row justify-between mt-4"> + <div class="font-bold"> + Pending + </div> + <div class="flex justify-center"> + </div> + </div> + + <div class="my-6 "> + <EvHistoryTable :readonly="false" :requests="pending" @deny="deny" @approve="approve" /> + </div> + + <AutoRules /> + + <div class="flex flex-row justify-between mt-16"> + <div class="font-bold"> + History + </div> + <div class="flex justify-center"> + <pagination :pages="pages" /> + </div> + </div> + + <div class="mt-1"> + <EvHistoryTable :readonly="true" :requests="permHistory" @deny="deny" @approve="approve" /> + </div> + + <div class="mt-4 ml-auto w-fit"> + <button class="rounded btn sm red" @click="clearHistory"> + Delete All + </button> + </div> + + <h3 class="text-center"> + History + </h3> + + <div class="mt-1 mb-20"> + <EventHistory /> + </div> + + </div> + </form> + </div> +</template> + +<script setup lang="ts"> +import { useConfirm } from '@vnuge/vnlib.browser'; +import { computed } from 'vue'; +import { get, useOffsetPagination } from '@vueuse/core'; +import { } from '@headlessui/vue' +import { useStore } from '../../store'; +import { CreateRuleType, PermissionRequest, PrStatus } from '../../../features'; +import EvHistoryTable from './EvHistoryTable.vue'; +import { filter, slice } from 'lodash'; +import AutoRules from './AutoRules.vue'; +import EventHistory from './EventHistory.vue'; + +const store = useStore() +const { reveal } = useConfirm() + +const pending = computed(() => store.permissions.pending) +const notPending = computed(() => filter(store.permissions.all, r => r.status !== PrStatus.Pending)) + +const deny = (request: PermissionRequest) => { + if(request.status !== PrStatus.Pending) return + //push deny to store + store.plugins.permission.deny(request.uuid) +} + +const approve = (request: PermissionRequest) => { + if(request.status !== PrStatus.Pending) return + //push allow to store + store.plugins.permission.allow(request.uuid, CreateRuleType.AllowOnce) +} + +const pages = useOffsetPagination({ + pageSize: 10, + total: computed(() => notPending.value.length) +}) + +const permHistory = computed(() => { + const start = (get(pages.currentPage) - 1) * get(pages.currentPageSize) + const end = start + 10 + return slice(notPending.value, start, end) +}) + +const clearHistory = async () => { + const { isCanceled } = await reveal({ + title: 'Clear History', + text: 'Are you sure you want to clear your event history?', + }) + + if(isCanceled) return + + //Clear all history + store.plugins.permission.clearRequests() +} + +</script> + +<style lang="scss"> +#ev-history{ + button.page-btn{ + @apply inline-flex items-center px-2 py-2 space-x-2 font-medium rounded-full; + @apply bg-white border border-gray-300 rounded-full hover:bg-gray-50 dark:bg-dark-600 dark:hover:bg-dark-500 dark:border-dark-300; + } + + form tr { + @apply sm:text-sm text-xs dark:text-gray-400 text-gray-600; + } +} +</style>
\ No newline at end of file diff --git a/extension/src/entries/options/components/AutoRules.vue b/extension/src/entries/options/components/AutoRules.vue index 16fddd3..9f46475 100644 --- a/extension/src/entries/options/components/AutoRules.vue +++ b/extension/src/entries/options/components/AutoRules.vue @@ -5,31 +5,10 @@ Approval Rules </div> <div class="flex justify-center"> - <nav aria-label="Pagination"> - <ul class="inline-flex items-center space-x-1 text-sm rounded-md"> - <li> - <button @click="prev" class="page-btn"> - <fa-icon icon="chevron-left" class="w-4" /> - </button> - </li> - <li> - <span class="inline-flex items-center px-4 py-2 space-x-1 rounded-md"> - Page - <b class="mx-1">{{ currentPage }}</b> - of - <b class="ml-1">{{ pageCount }}</b> - </span> - </li> - <li> - <button @click="next" class="page-btn"> - <fa-icon icon="chevron-right" class="w-4" /> - </button> - </li> - </ul> - </nav> + <pagination :pages="pages" /> </div> </div> - <div class=""> + <div class="mt-1"> <table class="min-w-full text-sm divide-y-2 divide-gray-200 dark:divide-dark-500"> <thead class="text-left bg-gray-50 dark:bg-dark-700"> <tr> @@ -40,7 +19,7 @@ Origin </th> <th class="p-2 font-medium whitespace-nowrap dark:text-white"> - Time + Expires </th> <th class="p-2"></th> </tr> @@ -52,10 +31,12 @@ {{ rule.type }} </td> <td class="p-2 whitespace-nowrap"> - {{ rule.origin }} + <a :href="rule.origin" target="_blank" class="text-blue-500 hover:underline"> + {{ rule.origin }} + </a> </td> <td class="p-2 whitespace-nowrap"> - {{ createShortDateAndTime(rule) }} + {{ getExpiration(rule) }} </td> <td class="p-2 text-right whitespace-nowrap"> <div class="button-group"> @@ -73,7 +54,7 @@ <script setup lang="ts"> import { computed } from 'vue'; -import { get, useOffsetPagination } from '@vueuse/core'; +import { formatTimeAgo, get, useOffsetPagination } from '@vueuse/core'; import { } from '@headlessui/vue' import { useStore } from '../../store'; import { storeToRefs } from 'pinia'; @@ -85,13 +66,13 @@ const { } = storeToRefs(store) const rules = computed(() => store.permissions.rules) -const { next, prev, currentPage, currentPageSize, pageCount } = useOffsetPagination({ +const pages = useOffsetPagination({ pageSize: 10, total: computed(() => rules.value.length) }) const currentRulePage = computed(() => { - const start = (get(currentPage) - 1) * get(currentPageSize) + const start = (get(pages.currentPage) - 1) * get(pages.currentPageSize) const end = start + 10 return slice(rules.value, start, end) }) @@ -100,18 +81,13 @@ const deleteRule = (rule: AutoAllowRule) => { store.plugins.permission.deleteRule(rule) } -const createShortDateAndTime = (request: { timestamp: number}) => { - const date = new Date(request.timestamp) - const hours = date.getHours() - const minutes = date.getMinutes() - const seconds = date.getSeconds() - const day = date.getDate() - const month = date.getMonth() + 1 - const year = date.getFullYear() - return `${month}/${day}/${year} ${hours}:${minutes}:${seconds}` +const getExpiration = (rule: AutoAllowRule) => { + if (!rule.expires) { + return "Never" + } + return formatTimeAgo(new Date(rule.expires)) } - </script> <style lang="scss"> diff --git a/extension/src/entries/options/components/EvHistoryTable.vue b/extension/src/entries/options/components/EvHistoryTable.vue index 6ea6cac..a0e2f30 100644 --- a/extension/src/entries/options/components/EvHistoryTable.vue +++ b/extension/src/entries/options/components/EvHistoryTable.vue @@ -1,5 +1,5 @@ <template> - <table class="min-w-full divide-y-2 divide-gray-200 dark:divide-dark-500"> + <table class="min-w-full divide-y-2 divide-gray-200 dark:divide-dark-500"> <thead class="text-left bg-gray-50 dark:bg-dark-700"> <tr> <th class="p-2 font-medium whitespace-nowrap dark:text-white"> @@ -21,7 +21,9 @@ {{ req.requestType }} </td> <td class="p-2 whitespace-nowrap"> - {{ req.origin }} + <a :href="req.origin" target="_blank" class="text-blue-500 hover:underline"> + {{ req.origin }} + </a> </td> <td class="p-2 whitespace-nowrap"> {{ createShortDateAndTime(req) }} diff --git a/extension/src/entries/options/components/EventHistory.vue b/extension/src/entries/options/components/EventHistory.vue index 0711ae6..b6cd13e 100644 --- a/extension/src/entries/options/components/EventHistory.vue +++ b/extension/src/entries/options/components/EventHistory.vue @@ -1,126 +1,152 @@ <template> - <div id="ev-history" class="flex flex-col w-full mt-4 sm:px-2"> - <form @submit.prevent=""> - <div class="w-full max-w-xl mx-auto"> - <h3 class="text-center"> - Permissions - </h3> - - <div class="flex flex-row justify-between mt-4"> - <div class="font-bold"> - Pending - </div> - <div class="flex justify-center"> - </div> - </div> - - <div class="my-6 "> - <EvHistoryTable :readonly="false" :requests="pending" @deny="deny" @approve="approve" /> - </div> - - <AutoRules /> - - <div class="flex flex-row justify-between mt-16"> - <div class="font-bold"> - History - </div> - <div class="flex justify-center"> - <nav aria-label="Pagination"> - <ul class="inline-flex items-center space-x-1 text-sm rounded-md"> - <li> - <button @click="prev" class="page-btn"> - <fa-icon icon="chevron-left" class="w-4" /> - </button> - </li> - <li> - <span class="inline-flex items-center px-4 py-2 space-x-1 rounded-md"> - Page - <b class="mx-1">{{ currentPage }}</b> - of - <b class="ml-1">{{ pageCount }}</b> - </span> - </li> - <li> - <button @click="next" class="page-btn"> - <fa-icon icon="chevron-right" class="w-4" /> - </button> - </li> - </ul> - </nav> - </div> - </div> - - <div class="mt-1"> - <EvHistoryTable :readonly="true" :requests="evHistoryCurrentPage" @deny="deny" @approve="approve" /> - </div> - - <div class="mt-4 ml-auto w-fit"> - <button class="rounded btn sm red" @click="clearHistory"> - Delete History - </button> - </div> + <div class=""> + <div class="flex flex-row justify-between mt-16"> + <div class="font-bold"> + Event History </div> - </form> + <div class="flex justify-center"> + <pagination :pages="pagination" /> + </div> + </div> + <div class=""> + <table class="min-w-full text-sm divide-y-2 divide-gray-200 dark:divide-dark-500"> + <thead class="text-left bg-gray-50 dark:bg-dark-700"> + <tr> + <th class="pl-2"></th> + <th class="p-2 font-medium whitespace-nowrap dark:text-white"> + Event + </th> + <th class="p-2 font-medium whitespace-nowrap dark:text-white"> + Time + </th> + <th class="p-2"></th> + </tr> + </thead> + + <tbody class="divide-y divide-gray-200 dark:divide-dark-500"> + <tr v-for="event in evHistory" :key="event.Id" class=""> + <td class="pl-2 whitespace-nowrap"> + <div class="flex flex-col items-end gap-0.5"> + <div class=""> + ID: + </div> + <div class=""> + EventId: + </div> + <div class=""> + PubKey: + </div> + <div class=""> + Content: + </div> + </div> + </td> + <td class="p-2"> + <div class="flex flex-col flex-1 gap-0.5"> + <div class="truncate overflow-ellipsis"> + {{ event.Id }} + </div> + + <div class="truncate overflow-ellipsis"> + {{ event.id }} + </div> + + <div class="truncate overflow-ellipsis"> + <a href="#" @click="goToKeyView(event)" class="text-blue-500 hover:underline"> + {{ event.pubkey }} + </a> + </div> + <div class="truncate overflow-ellipsis"> + {{ event.content }} + </div> + </div> + </td> + <td class="p-2 whitespace-nowrap"> + {{ timeAgo(event, timeStamp) }} + </td> + <td class="p-2 text-right whitespace-nowrap"> + <div class="button-group"> + <button class="rounded btn xs" @click="deleteEvent(event)"> + <fa-icon icon="trash" /> + </button> + </div> + </td> + </tr> + </tbody> + </table> + </div> </div> </template> <script setup lang="ts"> -import { useConfirm } from '@vnuge/vnlib.browser'; +import { apiCall } from '@vnuge/vnlib.browser'; import { computed } from 'vue'; -import { get, useOffsetPagination } from '@vueuse/core'; -import { } from '@headlessui/vue' +import { formatTimeAgo, get, useOffsetPagination, useTimestamp } from '@vueuse/core'; +import { } from '@headlessui/vue' import { useStore } from '../../store'; -import { PermissionRequest, PrStatus } from '../../../features'; -import EvHistoryTable from './EvHistoryTable.vue'; -import { filter, slice } from 'lodash'; -import AutoRules from './AutoRules.vue'; +import { EventEntry, NostrEvent } from '../../../features'; +import { map, slice } from 'lodash'; +import { useQuery } from '../../../features/util'; const store = useStore() -const { reveal } = useConfirm() - -const pending = computed(() => store.permissions.pending) -const notPending = computed(() => filter(store.permissions.all, r => r.status !== PrStatus.Pending)) -const deny = (request: PermissionRequest) => { - if(request.status !== PrStatus.Pending) return - //push deny to store - store.plugins.permission.deny(request.uuid) -} +const tabId = useQuery('t'); +const keyId = useQuery('kid'); -const approve = (request: PermissionRequest) => { - if(request.status !== PrStatus.Pending) return - //push allow to store - store.plugins.permission.allow(request.uuid, false) -} - -const { next, prev, currentPage, currentPageSize, pageCount } = useOffsetPagination({ +const pagination = useOffsetPagination({ pageSize: 10, - total: computed(() => notPending.value.length) + total: computed(() => store.eventHistory.length) }) -const evHistoryCurrentPage = computed(() => { - const start = (get(currentPage) - 1) * get(currentPageSize) +const explodeNote = (event: EventEntry) => JSON.parse(event.EventData) as NostrEvent + +const timeStamp = useTimestamp({interval: 1000}) + +const evHistory = computed<Array<NostrEvent & EventEntry>>(() => { + const start = (get(pagination.currentPage) - 1) * get(pagination.currentPageSize) const end = start + 10 - return slice(notPending.value, start, end) + const page = slice(store.eventHistory, start, end) + return map(page, event => { + const exploded = explodeNote(event) + return { + ...event, + ...exploded + } + }) }) -const clearHistory = async () => { - const { isCanceled } = await reveal({ - title: 'Clear History', - text: 'Are you sure you want to clear your event history?', - }) - if(isCanceled) return +const deleteEvent = (event: EventEntry) => { + //Call delete event function + apiCall(() => store.plugins.history.deleteEvent(event)) +} + +const createShortDateAndTime = (request: EventEntry) => { + const date = new Date(request.Created) + const hours = date.getHours() + const minutes = date.getMinutes() + const seconds = date.getSeconds() + const day = date.getDate() + const month = date.getMonth() + 1 + const year = date.getFullYear() + return `${month}/${day}/${year} ${hours}:${minutes}:${seconds}` +} + +const timeAgo = (entry: EventEntry, timeStamp: number) => { + return formatTimeAgo(new Date(entry.Created), { }, timeStamp) +} - //Clear all history - store.plugins.permission.clearRequests() +const goToKeyView = (key: { KeyId:string }) => { + //Show tab0 and set key + tabId.set('0') + keyId.set(key.KeyId); } </script> <style lang="scss"> -#ev-history{ - button.page-btn{ +#ev-history { + button.page-btn { @apply inline-flex items-center px-2 py-2 space-x-2 font-medium rounded-full; @apply bg-white border border-gray-300 rounded-full hover:bg-gray-50 dark:bg-dark-600 dark:hover:bg-dark-500 dark:border-dark-300; } diff --git a/extension/src/entries/options/components/Identities.vue b/extension/src/entries/options/components/Identities.vue index b7765be..8236b83 100644 --- a/extension/src/entries/options/components/Identities.vue +++ b/extension/src/entries/options/components/Identities.vue @@ -40,7 +40,7 @@ </div> <div class=""> <div class=""> - <button class="rounded btn sm" @click="store.refreshIdentities()"> + <button class="rounded btn sm" @click="identity.refreshKeys()"> <fa-icon icon="refresh" class="" /> </button> </div> @@ -89,8 +89,8 @@ <script setup lang="ts"> -import { isEqual, map } from 'lodash' -import { ref } from "vue"; +import { find, isEqual, map } from 'lodash' +import { computed, ref } from "vue"; import { Popover, PopoverButton, @@ -103,6 +103,7 @@ import { notify } from "@kyvg/vue3-notification"; import { get, useClipboard } from '@vueuse/core'; import { useStore } from '../../store'; import { storeToRefs } from 'pinia'; +import { useQuery } from '../../../features/util'; const emit = defineEmits(['edit-key']) @@ -111,13 +112,23 @@ configureNotifier({ notify, close: notify.close }) const downloadAnchor = ref<HTMLAnchorElement>() const store = useStore() -const { selectedKey, allKeys } = storeToRefs(store) +const { identity } = store.plugins +const { selectedKey } = storeToRefs(store) const { copy } = useClipboard() const { reveal } = useConfirm() +const query = useQuery('kid') const isSelected = (me : NostrPubKey) => isEqual(me, selectedKey.value) const editKey = (key : NostrPubKey) => emit('edit-key', key); -const selectKey = (key: NostrPubKey) => store.selectKey(key) +const selectKey = (key: NostrPubKey) => identity.selectKey(key) +const allKeys = computed(() => { + const q = query.get(); + if(q){ + const val = find(store.allKeys, k => k.Id === q || k.PubKey === q) + return val ? [val] : [] + } + return store.allKeys +}) const onCreate = async (e: Event, onClose : () => void) => { @@ -128,7 +139,7 @@ const onCreate = async (e: Event, onClose : () => void) => { await apiCall(async () => { //Create new identity - await store.createIdentity({ UserName, ExistingKey }) + await identity.createIdentity({ UserName, ExistingKey }) }) onClose() @@ -157,7 +168,7 @@ const onDeleteKey = async (key : NostrPubKey) => { apiCall(async ({ toaster }) => { //Delete identity - await store.deleteIdentity(key) + await identity.deleteIdentity(key) toaster.general.success({ 'title': 'Success', 'text': `${key.UserName} has been deleted` @@ -169,7 +180,7 @@ const onNip05Download = () => { apiCall(async () => { //Get all public keys from the server const keys = get(allKeys) - const nip05 = {} + const nip05 = {} as any; //Map the keys to the NIP-05 format map(keys, k => nip05[k.UserName] = k.PublicKey) //create file blob diff --git a/extension/src/entries/options/components/SiteSettings.vue b/extension/src/entries/options/components/SiteSettings.vue index 17f41c4..a2df205 100644 --- a/extension/src/entries/options/components/SiteSettings.vue +++ b/extension/src/entries/options/components/SiteSettings.vue @@ -47,6 +47,24 @@ </div> </div> </div> + <div class="mt-3"> + <div class="flex flex-row w-fit"> + <Switch + v-model="v$.authPopup.$model" + :class="v$.authPopup.$model ? 'bg-black dark:bg-white' : 'bg-gray-200 dark:bg-dark-600'" + class="relative inline-flex items-center h-5 mx-auto rounded-full w-11" + > + <span class="sr-only">Permissions Popup</span> + <span + :class="v$.authPopup.$model ? 'translate-x-6' : 'translate-x-1'" + class="inline-block w-4 h-4 transition transform rounded-full bg-gray-50 dark:bg-dark-900" + /> + </Switch> + <div class="my-auto ml-2 text-sm dark:text-gray-200"> + Permissions Popup + </div> + </div> + </div> </fieldset> </div> <h3 class="text-center"> @@ -165,6 +183,7 @@ const vRules = { maxLength: maxLength(50), alphaNum: helpers.withMessage('Nostr path is not a valid endpoint path that begins with /', path) }, + authPopup: {}, heartbeat: {}, } @@ -176,6 +195,7 @@ const [ editMode, toggleEdit ] = useToggle(false); const autoInject = computed(() => buffer.autoInject) const heartbeat = computed(() => buffer.heartbeat) +const authPopup = computed(() => buffer.authPopup) const onSave = async () => { @@ -218,6 +238,7 @@ const testConnection = async () =>{ //Watch for changes to autoinject value and publish changes when it does watchDebounced(autoInject, update, { debounce: 500, immediate: false }) watchDebounced(heartbeat, update, { debounce: 500, immediate: false }) +watchDebounced(authPopup, update, { debounce: 500, immediate: false }) </script> diff --git a/extension/src/entries/options/main.js b/extension/src/entries/options/main.js index 3dd01cb..901dfdd 100644 --- a/extension/src/entries/options/main.js +++ b/extension/src/entries/options/main.js @@ -19,6 +19,7 @@ import App from "./App.vue"; import '@fontsource/noto-sans-masaram-gondi' import "~/assets/all.scss"; import Notifications from "@kyvg/vue3-notification"; +import Pagination from '../../components/Pagination.vue'; /* FONT AWESOME CONFIG */ import { library } from '@fortawesome/fontawesome-svg-core' @@ -43,4 +44,5 @@ createApp(App) .use(Notifications) .use(pinia) .component('fa-icon', FontAwesomeIcon) + .component('pagination', Pagination) .mount("#app"); diff --git a/extension/src/entries/popup/Components/IdentitySelection.vue b/extension/src/entries/popup/Components/IdentitySelection.vue index eb08fb1..06d09a5 100644 --- a/extension/src/entries/popup/Components/IdentitySelection.vue +++ b/extension/src/entries/popup/Components/IdentitySelection.vue @@ -12,7 +12,7 @@ </select> </div> <div class="my-auto"> - <button class="btn sm borderless" @click="store.refreshIdentities()"> + <button class="btn sm borderless" @click="store.plugins.identity.refreshKeys()"> <fa-icon icon="refresh" class="" /> </button> </div> @@ -35,7 +35,7 @@ const onSelected = async ({target}) =>{ //Select the key of the given id const selected = find(allKeys.value, {Id: target.value}) if(selected){ - await store.selectKey(selected) + await store.plugins.identity.selectKey(selected) } } diff --git a/extension/src/entries/store/features.ts b/extension/src/entries/store/features.ts index ad83e16..d714ac6 100644 --- a/extension/src/entries/store/features.ts +++ b/extension/src/entries/store/features.ts @@ -68,7 +68,7 @@ const usePlugins = (context: ChannelContext) => { export const useBackgroundPiniaPlugin = (context: ChannelContext) => { //Create port for context const plugins = usePlugins(context) - const { user } = plugins; + const { user, settings, history } = plugins; //Plugin store return ({ store }: PiniaPluginContext) => { @@ -84,10 +84,15 @@ export const useBackgroundPiniaPlugin = (context: ChannelContext) => { }, { immediate: true }) //Wait for settings changes - onWatchableChange(plugins.settings, async () => { + onWatchableChange(settings, async () => { //Update settings and dark mode on change - store.settings = await plugins.settings.getSiteConfig(); - store.darkMode = await plugins.settings.getDarkMode(); + store.settings = await settings.getSiteConfig(); + store.darkMode = await settings.getDarkMode(); + }, { immediate: true }) + + onWatchableChange(history, async () => { + //Load event history + store.eventHistory = await history.getEvents(); }, { immediate: true }) return{ diff --git a/extension/src/entries/store/identity.ts b/extension/src/entries/store/identity.ts index ade7c94..ed01f6b 100644 --- a/extension/src/entries/store/identity.ts +++ b/extension/src/entries/store/identity.ts @@ -22,12 +22,7 @@ import { shallowRef } from 'vue'; declare module 'pinia' { export interface PiniaCustomStateProperties { allKeys: NostrPubKey[]; - selectedKey: NostrPubKey | undefined; - deleteIdentity(key: Partial<NostrPubKey>): Promise<void>; - createIdentity(id: Partial<NostrPubKey>): Promise<NostrPubKey>; - updateIdentity(id: NostrPubKey): Promise<NostrPubKey>; - selectKey(key: NostrPubKey): Promise<void>; - refreshIdentities(): Promise<void>; + selectedKey: NostrPubKey | undefined; } } diff --git a/extension/src/entries/store/index.ts b/extension/src/entries/store/index.ts index 8be57ff..0b4d3cd 100644 --- a/extension/src/entries/store/index.ts +++ b/extension/src/entries/store/index.ts @@ -31,8 +31,9 @@ export const useStore = defineStore({ state: (): NostrStoreState =>({ loggedIn: false, userName: '', - settings: undefined as any, - darkMode: false + settings: {} as any, + darkMode: false, + eventHistory: [], }), actions: { diff --git a/extension/src/entries/store/types.ts b/extension/src/entries/store/types.ts index 7addda4..536cf04 100644 --- a/extension/src/entries/store/types.ts +++ b/extension/src/entries/store/types.ts @@ -1,9 +1,10 @@ import { } from "webextension-polyfill"; -import { PluginConfig } from "../../features"; +import type { PluginConfig, EventEntry } from "../../features"; export interface NostrStoreState { loggedIn: boolean; userName: string | null; settings: PluginConfig; darkMode: boolean; + eventHistory: EventEntry[]; }
\ No newline at end of file diff --git a/extension/src/features/auth-api.ts b/extension/src/features/auth-api.ts index fbc9420..4e4d0ed 100644 --- a/extension/src/features/auth-api.ts +++ b/extension/src/features/auth-api.ts @@ -22,7 +22,7 @@ import { IMfaFlowContinuiation, totpMfaProcessor, useMfaLogin, usePkiAuth, useSe } from "@vnuge/vnlib.browser"; import { type FeatureApi, type BgRuntime, type IFeatureExport, exportForegroundApi, popupAndOptionsOnly, popupOnly } from "./framework"; import { waitForChangeFn } from "./util"; -import type { ClientStatus } from "./types"; +import type { ClientStatus, Watchable } from "./types"; import type { AppSettings } from "./settings"; import type { JsonObject } from "type-fest"; @@ -39,12 +39,11 @@ export interface ApiMessageHandler<T extends JsonObject> { (message: T, apiHandle: { axios: AxiosInstance }): Promise<any> } -export interface UserApi extends FeatureApi { +export interface UserApi extends FeatureApi, Watchable { login(username: string, password?: string): Promise<boolean> logout: () => Promise<void> getProfile: () => Promise<any> getStatus: () => Promise<ClientStatus> - waitForChange: () => Promise<void> submitMfa: (submission: IMfaSubmission) => Promise<boolean> } diff --git a/extension/src/features/framework/index.ts b/extension/src/features/framework/index.ts index b545335..755d27e 100644 --- a/extension/src/features/framework/index.ts +++ b/extension/src/features/framework/index.ts @@ -25,6 +25,7 @@ export interface BgRuntime<T> { readonly state: T; onInstalled(callback: () => Promise<void>): void; onConnected(callback: () => Promise<void>): void; + openBackChannel<T extends FeatureApi>(name: string, callback: (feature: T | undefined) => void): void; } export type FeatureApi = { @@ -84,6 +85,8 @@ export const protectMethod = <T extends Function>(func: T, ...protection: Channe return func; } +type BgCallback = (feature: FeatureApi | undefined) => void + /** * Creates a background runtime context for registering background * script feature api handlers @@ -92,12 +95,26 @@ export const useBackgroundFeatures = <TState>(state: TState): IBackgroundWrapper const { openOnMessageChannel } = createMessageChannel('background'); const { onMessage } = openOnMessageChannel() + + const backChannels = new Map<string, BgCallback>() + + const openBackChannel = async (name: string, callback: BgCallback) => { + backChannels.set(name, callback) + } + const notifyBackChannels = (pool: Map<string, FeatureApi>) => { + //Loop through all features + for (const [name, waiter] of backChannels.entries()){ + //Notify the waiter of the feature + waiter(pool.get(name)) + } + } const rt = { state, onConnected: runtime.onConnect.addListener, onInstalled: runtime.onInstalled.addListener, + openBackChannel } as BgRuntime<TState> /** @@ -109,12 +126,18 @@ export const useBackgroundFeatures = <TState>(state: TState): IBackgroundWrapper return{ register: <TFeature extends FeatureApi>(features: FeatureConstructor<TState, TFeature>[]) => { + + const featurePool = new Map<string, FeatureApi>() + //Loop through features for (const feature of features) { //Init feature const f = feature().background(rt) + //Add to pool + featurePool.set(feature.name, f) + //Get all exported function for (const externFuncName in f) { @@ -155,6 +178,9 @@ export const useBackgroundFeatures = <TState>(state: TState): IBackgroundWrapper }); } } + + //Notify all back channels that the load is complete + notifyBackChannels(featurePool) } } } diff --git a/extension/src/features/history.ts b/extension/src/features/history.ts index 82f31c4..e00a9af 100644 --- a/extension/src/features/history.ts +++ b/extension/src/features/history.ts @@ -13,29 +13,67 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -import { ref } from "vue"; -import { } from "./types"; -import { FeatureApi, BgRuntime, IFeatureExport } from "./framework"; +import { shallowRef } from "vue"; +import { watchDebounced, set, get, useToggle } from '@vueuse/core' +import { EventEntry, NostrEvent, Watchable } from "./types"; +import { FeatureApi, BgRuntime, IFeatureExport, exportForegroundApi, optionsOnly } from "./framework"; import { AppSettings } from "./settings"; +import { waitForChangeFn } from "./util"; +import { Endpoints } from "./server-api"; +import { useSession } from "@vnuge/vnlib.browser"; +import { } from "lodash"; -export interface HistoryEvent extends Object{ - +export interface SignedNEvent extends NostrEvent { + readonly signature: string } -export interface HistoryApi extends FeatureApi{ - +export interface HistoryApi extends FeatureApi, Watchable{ + getEvents: () => Promise<EventEntry[]>; + deleteEvent: (entry: EventEntry) => Promise<void>; + refresh: () => Promise<void>; } export const useHistoryApi = () : IFeatureExport<AppSettings, HistoryApi> => { return{ - background: ({ }: BgRuntime<AppSettings>): HistoryApi =>{ - const evHistory = ref([]); + background: ({ state }: BgRuntime<AppSettings>): HistoryApi =>{ + const { loggedIn } = useSession(); + const { execRequest } = state.useServerApi(); + const [ onRefresh, refresh ] = useToggle() + + const history = shallowRef<EventEntry[]>([]); + + //Watch for login changes and manual refreshes + watchDebounced([loggedIn, onRefresh], async ([li]) => { + + if(!li){ + set(history, []) + return + } + + //load history from server + history.value = await execRequest(Endpoints.GetHistory); + + }, { debounce: 1000 }) + + return{ + waitForChange:waitForChangeFn([history]), - return{ } + getEvents: () => Promise.resolve(history.value), + deleteEvent: optionsOnly(async (entry: EventEntry) => { + await execRequest(Endpoints.DeleteSingleEvent, entry) + refresh() + }), + refresh () { + refresh() + return Promise.resolve() + } + } }, - foreground: (): HistoryApi =>{ - return { } - } + foreground: exportForegroundApi<HistoryApi>([ + 'waitForChange', + 'getEvents', + 'deleteEvent', + ]) } } diff --git a/extension/src/features/identity-api.ts b/extension/src/features/identity-api.ts index 0b8973d..73f7ab2 100644 --- a/extension/src/features/identity-api.ts +++ b/extension/src/features/identity-api.ts @@ -28,10 +28,10 @@ import { shallowRef } from "vue"; import { useSession } from "@vnuge/vnlib.browser"; import { set, useToggle, watchDebounced } from "@vueuse/core"; import { isArray } from "lodash"; -import { waitForChange, waitForChangeFn } from "./util"; +import { waitForChangeFn } from "./util"; export interface IdentityApi extends FeatureApi, Watchable { - createIdentity: (identity: NostrPubKey) => Promise<NostrPubKey> + createIdentity: (identity: Partial<NostrPubKey>) => Promise<NostrPubKey> updateIdentity: (identity: NostrPubKey) => Promise<NostrPubKey> deleteIdentity: (key: NostrPubKey) => Promise<void> getAllKeys: () => Promise<NostrPubKey[]>; @@ -65,9 +65,7 @@ export const useIdentityApi = (): IFeatureExport<AppSettings, IdentityApi> => { selectedKey.value = undefined; } - //Wait for changes to trigger a new key-load - await waitForChange([ loggedIn, onKeyUpdateTriggered ]) - }, { debounce: 100 }) + }, { debounce: 100, immediate: true }) return { //Identity is only available in options context diff --git a/extension/src/features/index.ts b/extension/src/features/index.ts index 0a8e182..d7e1b05 100644 --- a/extension/src/features/index.ts +++ b/extension/src/features/index.ts @@ -14,7 +14,7 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. //Export all shared types -export type { NostrPubKey, LoginMessage } from './types' +export type { NostrPubKey, LoginMessage, NostrEvent, NostrRelay, EventEntry } from './types' export type * from './framework' export type { PluginConfig } from './settings' export type { PkiPubKey, EcKeyParams, LocalPkiApi as PkiApi } from './pki-api' @@ -33,6 +33,6 @@ export { useSettingsApi, useAppSettings } from './settings' export { useHistoryApi } from './history' export { useEventTagFilterApi } from './tagfilter-api' export { useInjectAllowList } from './nip07allow-api' -export { onWatchableChange } from './util' +export { onWatchableChange, waitOne, useQuery } from './util' export { useMfaConfigApi } from './mfa-api' -export { usePermissionApi, PrStatus } from './permissions'
\ No newline at end of file +export { usePermissionApi, PrStatus, CreateRuleType } from './permissions'
\ No newline at end of file diff --git a/extension/src/features/nip07allow-api.ts b/extension/src/features/nip07allow-api.ts index 4787437..89639d1 100644 --- a/extension/src/features/nip07allow-api.ts +++ b/extension/src/features/nip07allow-api.ts @@ -20,7 +20,7 @@ import { BgRuntime, FeatureApi, IFeatureExport, exportForegroundApi, popupAndOpt import { AppSettings } from "./settings"; import { set, get, toRefs } from "@vueuse/core"; import { computed, shallowRef } from "vue"; -import { waitForChangeFn } from "./util"; +import { waitForChangeFn, push, remove } from "./util"; interface AllowedSites{ origins: string[]; @@ -99,7 +99,7 @@ export const useInjectAllowList = (): IFeatureExport<AppSettings, InjectAllowlis //See if origin is already in the list if (!includes(origins.value, originOnly)) { //Add to the list - origins.value.push(originOnly); + push(origins, originOnly); //If current tab was added, reload the tab if (!origin) { @@ -117,10 +117,9 @@ export const useInjectAllowList = (): IFeatureExport<AppSettings, InjectAllowlis //Get origin part of url const delOriginOnly = new URL(delOrigin).origin - const allowList = get(origins) //Remove the origin - origins.value = filter(allowList, (o) => !isEqual(o, delOriginOnly)); + remove(origins, delOriginOnly) //If current tab was removed, reload the tab if (!origin) { diff --git a/extension/src/features/nostr-api.ts b/extension/src/features/nostr-api.ts index 4aee660..7f1bedf 100644 --- a/extension/src/features/nostr-api.ts +++ b/extension/src/features/nostr-api.ts @@ -17,8 +17,9 @@ import { cloneDeep } from "lodash"; import { Endpoints } from "./server-api"; import { type FeatureApi, type BgRuntime, type IFeatureExport, optionsOnly, exportForegroundApi } from "./framework"; import { type AppSettings } from "./settings"; -import { useTagFilter } from "./tagfilter-api"; +import { EventTagFilterApi } from "./tagfilter-api"; import type { NostrRelay, EncryptionRequest, NostrEvent } from './types'; +import { HistoryApi } from "./history"; /** @@ -36,10 +37,16 @@ export interface NostrApi extends FeatureApi { export const useNostrApi = (): IFeatureExport<AppSettings, NostrApi> => { return{ - background: ({ state }: BgRuntime<AppSettings>) =>{ + background: ({ state, openBackChannel }: BgRuntime<AppSettings>) =>{ const { execRequest } = state.useServerApi(); - const { filterTags } = useTagFilter(state) + + let tagFilter: EventTagFilterApi | undefined; + let evHistory: HistoryApi | undefined; + + //Register for tag filter, and history back channel + openBackChannel<EventTagFilterApi>('useEventTagFilterApi', (feature) => tagFilter = feature) + openBackChannel<HistoryApi>('useHistoryApi', (feature) => evHistory = feature) return { getRelays: async (): Promise<NostrRelay[]> => { @@ -50,16 +57,21 @@ export const useNostrApi = (): IFeatureExport<AppSettings, NostrApi> => { signEvent: async (req: NostrEvent): Promise<NostrEvent | undefined> => { //Store copy to prevent mutation - req = cloneDeep(req) + const event = cloneDeep(req) //If tag filter is enabled, filter before continuing - if(state.currentConfig.value.tagFilter){ - await filterTags(req) + if (tagFilter){ + //Filter tags + await tagFilter.filterTags(event); } //Sign the event - const event = await execRequest(Endpoints.SignEvent, req); - return event; + const result = await execRequest(Endpoints.SignEvent, event); + + //Refresh history + evHistory?.refresh(); + + return result; }, nip04Encrypt: async (data: EncryptionRequest): Promise<string> => { const message: EncryptionRequest = { diff --git a/extension/src/features/permissions.ts b/extension/src/features/permissions.ts index 5473962..82b023f 100644 --- a/extension/src/features/permissions.ts +++ b/extension/src/features/permissions.ts @@ -13,13 +13,13 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -import { Mutable, get, set, toRefs } from "@vueuse/core"; +import { Mutable, get, set, toRefs, useTimestamp } from "@vueuse/core"; import { Ref } from "vue"; import { defaultTo, defaults, defer, filter, find, forEach, isEqual, isNil } from "lodash"; import { nanoid } from "nanoid"; -import { useSession } from "@vnuge/vnlib.browser"; +import { debugLog, useSession } from "@vnuge/vnlib.browser"; import { type FeatureApi, type BgRuntime, type IFeatureExport, exportForegroundApi, optionsOnly } from "./framework"; -import { waitForChangeFn, waitOne } from "./util"; +import { remove, waitForChangeFn, waitOne } from "./util"; import { windows, runtime, Windows, tabs } from "webextension-polyfill"; import type { TotpUpdateMessage, Watchable } from "./types"; import type { AppSettings } from "./settings"; @@ -27,6 +27,7 @@ import type { AppSettings } from "./settings"; export interface AutoAllowRule{ origin: string type: string + readonly expires?: number readonly timestamp: number } @@ -37,6 +38,16 @@ export enum PrStatus{ Denied } +export enum CreateRuleType{ + AllowOnce, + AllowForever, + FiveMinutes, + OneHour, + OneDay, + OneWeek, + OneMonth, +} + export interface PermissionRequest{ readonly uuid: string readonly origin: string @@ -49,7 +60,7 @@ export type MfaUpdateResult = TotpUpdateMessage export interface PermissionApi extends FeatureApi, Watchable { getRequests(): Promise<PermissionRequest[]> - allow(requestId: string, addRule: boolean): Promise<void> + allow(requestId: string, addRule: CreateRuleType): Promise<void> deny(requestId: string): Promise<void> clearRequests(): Promise<void> requestAndWaitResult(request: Partial<PermissionRequest>): Promise<PrStatus> @@ -67,6 +78,24 @@ interface RuleSlot{ rules: AutoAllowRule[] } +const setExpirationRule = (expType: CreateRuleType): { expires?: number } => { + switch (expType) { + case CreateRuleType.AllowOnce: + case CreateRuleType.AllowForever: + return { } + case CreateRuleType.FiveMinutes: + return { expires: Date.now() + (5 * 60 * 1000) }; + case CreateRuleType.OneHour: + return { expires: Date.now() + (60 * 60 * 1000) }; + case CreateRuleType.OneDay: + return { expires: Date.now() + (24 * 60 * 60 * 1000) }; + case CreateRuleType.OneWeek: + return { expires: Date.now() + (7 * 24 * 60 * 60 * 1000) }; + case CreateRuleType.OneMonth: + return { expires: Date.now() + (30 * 24 * 60 * 60 * 1000) }; + } +} + const useRuleSet = (slot: Ref<RuleSlot>) => { defaults(slot.value, { rules: [] }) @@ -76,6 +105,14 @@ const useRuleSet = (slot: Ref<RuleSlot>) => { isAllowed: (request: PermissionRequest): boolean => { //find existing rule const rule = find(get(rules), r => isEqual(r.origin, request.origin) && isEqual(r.type, request.requestType)) + + //See if rule exists and is expired + if (rule && rule.expires && rule.expires < Date.now()) { + //remove expired rule + remove(rules, rule) + return false + } + return !isNil(rule) }, addRule: (rule: Partial<AutoAllowRule>) => { @@ -99,7 +136,12 @@ const useRuleSet = (slot: Ref<RuleSlot>) => { const wo = filter(get(rules), r => !(isEqual(r.origin, rule.origin) && isEqual(r.type, rule.type))) set(rules, wo) }, - getRules:(): AutoAllowRule[] =>get(rules) + getRules:(): AutoAllowRule[] => { + //Filter all expired rules + const wo = filter(get(rules), r => !r.expires || r.expires > Date.now()) + set(rules, wo) + return wo + } } } @@ -110,41 +152,13 @@ const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useR defaults(slot.value, { rules: [] }) const { requests } = toRefs(slot) - const drawWindow = async ({ uuid }: Partial<PermissionRequest>): Promise<Windows.CreateCreateDataType> => { - const current = await windows.getCurrent() - - const minWidth = 350 - const minHeight = 180 - - const maxWidth = 500 - const maxHeight = 250 - - const width = Math.min(Math.max(current.width! - 100, minWidth), maxWidth) - const height = Math.min(Math.max(current.height! - 100, minHeight), maxHeight) - - //draw half way across screen minus half its width - const left = current.left! + (current.width! / 2) - (width / 2) - - return { - url: `${permPopupUrl}?uuid=${uuid}&closeable`, - type: "popup", - height: height, - width: width, - focused: true, - allowScriptsToClose: true, - top: 100, - //try to center popup - left: left, - } - } - const activePopups = new Map<number, PermissionRequest>() const getRequest = (requestId: string): PermissionRequest | undefined => { return find(get(requests), r => r.uuid === requestId) } - const updateRequest = (request: PermissionRequest, addRule: boolean) => { + const updateRequest = (request: PermissionRequest, addRule: CreateRuleType) => { const current = get(requests) const index = current.findIndex(r => r.uuid === request.uuid) @@ -159,8 +173,14 @@ const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useR set(requests, current) //Add rule if needed - if (addRule) { - rules.addRule({ origin: request.origin, type: request.requestType }) + switch (addRule) { + case CreateRuleType.AllowOnce: + //Do nothing + break; + //Compute expiration + default: + const { expires } = setExpirationRule(addRule); + rules.addRule({ origin: request.origin, type: request.requestType, expires }) } } @@ -173,6 +193,41 @@ const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useR } as PermissionRequest } + const showPermsWindow = async (request: PermissionRequest): Promise<void> => { + + const drawWindow = async ({ uuid }: Partial<PermissionRequest>): Promise<Windows.CreateCreateDataType> => { + const current = await windows.getCurrent() + + const minWidth = 350 + const minHeight = 180 + + const maxWidth = 500 + const maxHeight = 250 + + const width = Math.min(Math.max(current.width! - 100, minWidth), maxWidth) + const height = Math.min(Math.max(current.height! - 100, minHeight), maxHeight) + + //draw half way across screen minus half its width + const left = current.left! + (current.width! / 2) - (width / 2) + + return { + url: `${permPopupUrl}?uuid=${uuid}&closeable`, + type: "popup", + height: height, + width: width, + focused: true, + allowScriptsToClose: true, + top: 100, + //try to center popup + left: left, + } + } + + const windowsArgs = await drawWindow(request) + const { id } = await windows.create(windowsArgs) + activePopups.set(id!, request) + } + //Listen for popup close to cleanup request windows.onRemoved.addListener(async (id) => { const req = activePopups.get(id) @@ -199,12 +254,6 @@ const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useR return{ getRequest, - async showPermsWindow (request: PermissionRequest): Promise<void> { - const windowsArgs = await drawWindow(request) - const { id } = await windows.create(windowsArgs) - activePopups.set(id!, request) - }, - pushRequest (request: Partial<PermissionRequest>, showPopup: boolean): PermissionRequest { //Create new request const req = initNewRequest(request) @@ -223,13 +272,13 @@ const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useR //Show popup if needed if (showPopup) { - this.showPermsWindow(req) + showPermsWindow(req) } return req }, - allow (requestId: string, addRule: boolean): void { + allow(requestId: string, addRule: CreateRuleType): void { const request = getRequest(requestId) if(!request){ throw new Error("Request not found") @@ -248,7 +297,7 @@ const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useR //set denied (request as Mutable<PermissionRequest>).status = PrStatus.Denied //update request - updateRequest(request, false) + updateRequest(request, CreateRuleType.AllowOnce) }, clearAll: () => { @@ -257,12 +306,13 @@ const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useR //set denied (r as Mutable<PermissionRequest>).status = PrStatus.Denied //update request - updateRequest(r, false) + updateRequest(r, CreateRuleType.AllowOnce) }) //Then defer clear defer(() => set(requests, [])) }, + getAll: () => get(requests) } } @@ -282,8 +332,11 @@ export const usePermissionApi = (): IFeatureExport<AppSettings, PermissionApi> = const ruleSet = useRuleSet(ruleStore) const permissions = usePermissions(reqStore, ruleSet) + //Computed current time to trigger an update every second + const currentTime = useTimestamp({ interval: 1000 }) + return { - waitForChange: waitForChangeFn([currentConfig, loggedIn, reqStore, ruleStore]), + waitForChange: waitForChangeFn([currentConfig, loggedIn, reqStore, ruleStore, currentTime]), getRequests: () => Promise.resolve(permissions.getAll()), @@ -292,7 +345,7 @@ export const usePermissionApi = (): IFeatureExport<AppSettings, PermissionApi> = return Promise.resolve() }, - allow(requestId: string, addRule: boolean) { + allow(requestId: string, addRule: CreateRuleType) { permissions.allow(requestId, addRule) return Promise.resolve() }, @@ -304,8 +357,11 @@ export const usePermissionApi = (): IFeatureExport<AppSettings, PermissionApi> = }), async requestAndWaitResult(request: Partial<PermissionRequest>) { - //push request - const req = permissions.pushRequest(request, true) + + debugLog("Requesting permission", request) + + //push request and show popup only if enabled + const req = permissions.pushRequest(request, currentConfig.value.authPopup) //See if pending if(req.status !== PrStatus.Pending){ @@ -316,7 +372,7 @@ export const usePermissionApi = (): IFeatureExport<AppSettings, PermissionApi> = do { //wait for a change - await waitOne([reqStore]) + await waitOne([ reqStore ]) //check if request was approved const status = permissions.getRequest(req.uuid); diff --git a/extension/src/features/server-api/index.ts b/extension/src/features/server-api/index.ts index b9524ed..35bed6f 100644 --- a/extension/src/features/server-api/index.ts +++ b/extension/src/features/server-api/index.ts @@ -19,7 +19,7 @@ import { get } from '@vueuse/core' import { type WebMessage, type UserProfile } from "@vnuge/vnlib.browser" import { initEndponts } from "./endpoints" import { cloneDeep } from "lodash" -import type { EncryptionRequest, NostrEvent, NostrPubKey, NostrRelay } from "../types" +import type { EncryptionRequest, EventEntry, NostrEvent, NostrPubKey, NostrRelay } from "../types" export enum Endpoints { GetKeys = 'getKeys', @@ -32,6 +32,8 @@ export enum Endpoints { CreateId = 'createIdentity', UpdateId = 'updateIdentity', UpdateProfile = 'updateProfile', + GetHistory = 'getEvents', + DeleteSingleEvent = 'deleteSingleEvent', } export interface ExecRequestHandler{ @@ -45,6 +47,8 @@ export interface ExecRequestHandler{ (id: Endpoints.CreateId, identity: NostrPubKey):Promise<NostrPubKey> (id: Endpoints.UpdateId, identity: NostrPubKey):Promise<NostrPubKey> (id: Endpoints.UpdateProfile, profile: UserProfile):Promise<string> + (id: Endpoints.GetHistory):Promise<EventEntry[]> + (id: Endpoints.DeleteSingleEvent, evntId: EventEntry):Promise<void> } export interface ServerApi{ @@ -65,7 +69,7 @@ export const useServerApi = (nostrUrl: Ref<string>, accUrl: Ref<string>): Server registerEndpoint({ id: Endpoints.DeleteKey, method: 'DELETE', - path: (key: NostrPubKey) => `${get(nostrUrl)}?type=identity&key_id=${key.Id}`, + path: (key: NostrPubKey) => `${get(nostrUrl)}?type=identity&id=${key.Id}`, onRequest: () => Promise.resolve(), onResponse: async (response: WebMessage) => response.getResultOrThrow() }) @@ -147,5 +151,22 @@ export const useServerApi = (nostrUrl: Ref<string>, accUrl: Ref<string>): Server onResponse: async (response: WebMessage<string>) => response.getResultOrThrow() }) + //History api + registerEndpoint({ + id: Endpoints.GetHistory, + method: 'GET', + path: () => `${get(nostrUrl)}?type=getEvents`, + onRequest: () => Promise.resolve(), + onResponse: (response : EventEntry[]) => Promise.resolve(response) //Pass through response, should be an array of events or an error + }) + + registerEndpoint({ + id: Endpoints.DeleteSingleEvent, + method: 'DELETE', + path: (evnt: EventEntry) => `${get(nostrUrl)}?type=event&id=${evnt.Id}`, + onRequest: () => Promise.resolve(), + onResponse: (response) => Promise.resolve(response) + }) + return { execRequest } }
\ No newline at end of file diff --git a/extension/src/features/settings.ts b/extension/src/features/settings.ts index ca714a5..0bd7101 100644 --- a/extension/src/features/settings.ts +++ b/extension/src/features/settings.ts @@ -14,7 +14,7 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. import { storage } from "webextension-polyfill" -import { } from 'lodash' +import { defaultsDeep } from 'lodash' import { configureApi, debugLog } from '@vnuge/vnlib.browser' import { MaybeRefOrGetter, readonly, Ref, shallowRef, watch } from "vue"; import { JsonObject } from "type-fest"; @@ -30,18 +30,20 @@ export interface PluginConfig extends JsonObject { readonly nostrEndpoint: string; readonly heartbeat: boolean; readonly maxHistory: number; - readonly tagFilter: boolean, + readonly tagFilter: boolean; + readonly authPopup: boolean; } //Default storage config -const defaultConfig : PluginConfig = { +const defaultConfig : PluginConfig = Object.freeze({ apiUrl: import.meta.env.VITE_API_URL, accountBasePath: import.meta.env.VITE_ACCOUNTS_BASE_PATH, nostrEndpoint: import.meta.env.VITE_NOSTR_ENDPOINT, heartbeat: import.meta.env.VITE_HEARTBEAT_ENABLED === 'true', maxHistory: 50, tagFilter: true, -}; + authPopup: true, +}); export interface AppSettings{ saveConfig(config: PluginConfig): void; @@ -62,6 +64,9 @@ export const useAppSettings = (): AppSettings => { const _storageBackend = storage.local; const store = useStorage<PluginConfig>(_storageBackend, 'siteConfig', defaultConfig); + //Merge the default config for nullables with the current config on startyup + defaultsDeep(store.value, defaultConfig); + watch(store, (config, _) => { //Configure the vnlib api configureApi({ diff --git a/extension/src/features/tagfilter-api.ts b/extension/src/features/tagfilter-api.ts index 22369d0..b8778a1 100644 --- a/extension/src/features/tagfilter-api.ts +++ b/extension/src/features/tagfilter-api.ts @@ -17,8 +17,8 @@ import { TaggedNostrEvent, Watchable } from "./types"; import { filter, isEmpty, isEqual, isRegExp } from "lodash"; import { BgRuntime, FeatureApi, IFeatureExport, exportForegroundApi } from "./framework"; import { AppSettings } from "./settings"; -import { get, toRefs } from "@vueuse/core"; -import { waitForChangeFn } from "./util"; +import { get, toRefs, set } from "@vueuse/core"; +import { push, remove, waitForChangeFn } from "./util"; interface EventTagFilteStorage { filters: string[]; @@ -28,98 +28,90 @@ interface EventTagFilteStorage { export interface EventTagFilterApi extends FeatureApi, Watchable { filterTags(event: TaggedNostrEvent): Promise<void>; addFilter(tag: string): Promise<void>; + addFilter(tags: string[]): Promise<void>; removeFilter(tag: string): Promise<void>; - addFilters(tags: string[]): Promise<void>; isEnabled(): Promise<boolean>; enable(value:boolean): Promise<void>; } -export const useTagFilter = (settings: AppSettings): EventTagFilterApi => { - //use storage - const store = settings.useStorageSlot<EventTagFilteStorage>('tag-filter-struct', { filters: [], enabled: false }); - const { filters, enabled } = toRefs(store) - - return { - waitForChange: waitForChangeFn([filters, enabled]), - filterTags: async (event: TaggedNostrEvent): Promise<void> => { - - if(!event.tags){ - return; - } +export const useEventTagFilterApi = (): IFeatureExport<AppSettings, EventTagFilterApi> => { + return{ + background: ({ state }: BgRuntime<AppSettings>) => { - if(isEmpty(event.tags)){ - return; - } + //use storage + const store = state.useStorageSlot<EventTagFilteStorage>('tag-filter-struct', { filters: [], enabled: false }); - /* - * Nostr events contain a nested array of tags, they may be any - * json type. The first element of the array should be the tag name - * and the rest of the array is the tag data. - */ - const allowedTags = filter(event.tags, ([tagName]) => { - if(!tagName){ - return false; - } + const { filters, enabled } = toRefs(store) - if(!filters.value.length){ - return true; - } + return{ + waitForChange: waitForChangeFn([filters, enabled]), + filterTags (event: TaggedNostrEvent): Promise<void> { - const asString = tagName.toString(); + if (!event.tags || isEmpty(event.tags)) { + return Promise.resolve(); + } - for (const filter of get(filters)) { - //if the filter is a regex, test it, if it fails, its allowed - if (isRegExp(filter)) { - if (filter.test(asString)) { + /* + * Nostr events contain a nested array of tags, they may be any + * json type. The first element of the array should be the tag name + * and the rest of the array is the tag data. + */ + const allowedTags = filter(event.tags, ([tagName]) => { + //May be an undefined tag, so ignore it + if (!tagName) { return false; } - } - //If the filter is a string, compare it, if it matches, it's not allowed - if (isEqual(filter, asString)) { - return false; - } - } - //Its allowed - return true; - }) + if (!filters.value.length) { + return true; + } + + const asString = tagName.toString(); - //overwrite tags array - event.tags = allowedTags; - }, - addFilter: async (tag: string) => { - //add new filter to list - filters.value.push(tag); - }, - removeFilter: async (tag: string) => { - //remove filter from list - filters.value = filter(filters.value, t => !isEqual(t, tag)); - }, - addFilters: async (tags: string[]) => { - //add new filters to list - filters.value.push(...tags); - }, - isEnabled: async () => { - return enabled.value; - }, - enable: async (value:boolean) => { - enabled.value = value; - } - } -} + for (const filter of get(filters)) { + //if the filter is a regex, test it, if it fails, its allowed + if (isRegExp(filter)) { + if (filter.test(asString)) { + return false; + } + } + //If the filter is a string, compare it, if it matches, it's not allowed + if (isEqual(filter, asString)) { + return false; + } + } -export const useEventTagFilterApi = (): IFeatureExport<AppSettings, EventTagFilterApi> => { - return{ - background: ({ state }: BgRuntime<AppSettings>) => { - return{ - ...useTagFilter(state) + //Its allowed + return true; + }) + + //overwrite tags array + event.tags = allowedTags; + return Promise.resolve(); + }, + addFilter(tags: string | string[]) { + //add new filter to list + push(filters, tags) + return Promise.resolve(); + }, + removeFilter(tag: string) { + //remove filter from list + remove(filters, tag); + return Promise.resolve(); + }, + isEnabled: () => Promise.resolve(enabled.value), + enable (value: boolean) { + set(enabled, value); + return Promise.resolve(); + } } }, foreground: exportForegroundApi([ 'filterTags', 'addFilter', 'removeFilter', - 'addFilters', + 'isEnabled', + 'enable' ]) } }
\ No newline at end of file diff --git a/extension/src/features/types.ts b/extension/src/features/types.ts index fe59011..4689ccc 100644 --- a/extension/src/features/types.ts +++ b/extension/src/features/types.ts @@ -15,14 +15,21 @@ import { JsonObject } from "type-fest"; -export interface NostrPubKey extends JsonObject { +export interface DbEntry extends JsonObject { readonly Id: string, - readonly UserName: string, - readonly PublicKey: string, readonly Created: string, readonly LastModified: string } +export interface NostrPubKey extends DbEntry { + readonly UserName: string, + readonly PublicKey: string, +} + +export interface EventEntry extends DbEntry { + readonly EventData: string +} + export interface NostrEvent extends JsonObject { KeyId: string, readonly id: string, @@ -48,12 +55,9 @@ export interface EncryptionRequest extends JsonObject { readonly pubkey: string } -export interface NostrRelay extends JsonObject { - readonly Id: string, - readonly url: string, - readonly flags: number, - readonly Created: string, - readonly LastModified: string +export interface NostrRelay extends DbEntry { + readonly url: string; + readonly flags: number; } export interface LoginMessage extends JsonObject { @@ -105,4 +109,5 @@ export interface TotpUpdateMessage extends JsonObject { readonly period: number readonly algorithm: string readonly secret: string -}
\ No newline at end of file +} + diff --git a/extension/src/features/util.ts b/extension/src/features/util.ts index 6ec8f15..53f6ffd 100644 --- a/extension/src/features/util.ts +++ b/extension/src/features/util.ts @@ -14,24 +14,25 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. -import { defer } from "lodash"; -import { RemovableRef, SerializerAsync, StorageLikeAsync, useStorageAsync, watchOnce } from "@vueuse/core"; -import { type MaybeRefOrGetter, type WatchSource, isProxy, toRaw } from "vue"; +import { defer, filter, isEqual } from "lodash"; +import { RemovableRef, SerializerAsync, StorageLikeAsync, useStorageAsync, watchOnce, get, set } from "@vueuse/core"; +import { type MaybeRefOrGetter, type WatchSource, isProxy, toRaw, MaybeRef, shallowRef } from "vue"; import type { Watchable } from "./types"; export const waitForChange = <T extends Readonly<WatchSource<unknown>[]>>(source: [...T]):Promise<void> => { - return new Promise((resolve) => watchOnce<any>(source, () => resolve(), { deep: true })) + return new Promise((resolve) => watchOnce<any>(source, () => defer(() => resolve()), { deep: true })) } export const waitForChangeFn = <T extends Readonly<WatchSource<unknown>[]>>(source: [...T]) => { - return (): Promise<void> => { - return new Promise((resolve) => watchOnce<any>(source, () => resolve(), {deep: true})) - } + return (): Promise<void> => waitForChange(source) } -export const waitOne = <T extends Readonly<WatchSource<unknown>[]>>(source: [...T]): Promise<void> => { - return new Promise((resolve) => watchOnce<any>(source, () => resolve(), { deep: true })) -} +/** + * Waits for a change to occur on the given watch source + * once. + * @returns A promise that resolves when the change occurs. + */ +export const waitOne = waitForChange; export const useStorage = <T>(storage: any & chrome.storage.StorageArea, key: string, initialValue: MaybeRefOrGetter<T>): RemovableRef<T> => { @@ -69,10 +70,10 @@ export const useStorage = <T>(storage: any & chrome.storage.StorageArea, key: st } } - return useStorageAsync<T>(key, initialValue, wrapper, { serializer, deep: true, shallow: true }); + return useStorageAsync<T>(key, initialValue, wrapper, { serializer, shallow: true }); } -export const onWatchableChange = (watchable: Watchable, onChangeCallback: () => Promise<any>, controls?: { immediate: boolean }) => { +export const onWatchableChange = ({ waitForChange }: Watchable, onChangeCallback: () => Promise<any>, controls?: { immediate: boolean }) => { defer(async () => { if (controls?.immediate) { @@ -80,8 +81,66 @@ export const onWatchableChange = (watchable: Watchable, onChangeCallback: () => } while (true) { - await watchable.waitForChange(); + await waitForChange(); await onChangeCallback(); } }) +} + +export const push = <T>(arr: MaybeRef<T[]>, item: T | T[]) => { + //get the reactuve value first + const current = get(arr) + if (Array.isArray(item)) { + //push the items + current.push(...item) + } else { + //push the item + current.push(item) + } + //set the value + set(arr, current) +} + +export const remove = <T>(arr: MaybeRef<T[]>, item: T) => { + //get the reactuve value first + const current = get(arr) + //Get all items that are not the item + const wo = filter(current, (i) => !isEqual(i, item)) + //set the value + set(arr, wo) +} + +export const useQuery = (query: string) => { + + const get = () => { + const args = new URLSearchParams(window.location.search) + return args.get(query) + } + + const set = (value: string) => { + const args = new URLSearchParams(window.location.search); + args.set(query, value); + (window as any).customHistory.replaceState({}, '', `${window.location.pathname}?${args.toString()}`) + } + + const mutable = shallowRef<string | null>(get()) + + //Setup custom historu + if (!('customHistory' in window)) { + (window as any).customHistory = { + replaceState: (...args: any[]) => { + window.history.replaceState(...args) + window.dispatchEvent(new Event('replaceState')) + } + } + } + + //Listen for custom history events and update the mutable state + window.addEventListener('replaceState', () => mutable.value = get()) + + return{ + get, + set, + asRef: mutable + } }
\ No newline at end of file |