Repository: web-scrobbler/web-scrobbler
Pull request #4033 (2023-08-17 05:00:57)

Improve onboarding and other UX
- Reimplements the notification telling user to sign in on startup if not signed in.
- Adds functionality to go to a specific part of options using URL.
  - Used in disabled connector popup item to go to connector list.
  - Used in service error popup item to go to accounts.
  - Used in onboarding notification to go to accounts.
- Also, one small unrelated change, reimplementing the code updating the version number in storage in case it's needed in the future, was missed in the V3 migration.

I think 3.1 should probably be released after this + pleroma and sirius PRs (and a quick sanity test of everything together, of course)


Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 91a50382..5e1a17f6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,13 +12,10 @@ jobs:
       - name: Checkout the repository
         uses: actions/checkout@v3
 
-      - name: Get Node.js version from .nvmrc
-        run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
-
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
 
       - name: Install dependencies
         run: npm ci
@@ -47,7 +44,7 @@ jobs:
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
 
       - name: Install dependencies
         run: npm ci
@@ -69,13 +66,10 @@ jobs:
       - name: Checkout the repository
         uses: actions/checkout@v3
 
-      - name: Get Node.js version from .nvmrc
-        run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
-
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
 
       - name: Install dependencies
         run: npm ci
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 58b96b1e..e54383b0 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -13,13 +13,10 @@ jobs:
       - name: Checkout the repository
         uses: actions/checkout@v3
 
-      - name: Get Node.js version from .nvmrc
-        run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
-
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
 
       - name: Install dependencies
         run: npm ci
@@ -43,13 +40,10 @@ jobs:
       - name: Checkout the repository
         uses: actions/checkout@v3
 
-      - name: Get Node.js version from .nvmrc
-        run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
-
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
 
       - name: Install dependencies
         run: npm ci
@@ -73,13 +67,10 @@ jobs:
       - name: Checkout the repository
         uses: actions/checkout@v3
 
-      - name: Get Node.js version from .nvmrc
-        run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
-
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
 
       - name: Install dependencies
         run: npm ci
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 2c48094f..4f86951c 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -12,13 +12,10 @@ jobs:
       - name: Checkout the repository
         uses: actions/checkout@v3
 
-      - name: Get Node.js version from .nvmrc
-        run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
-
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
 
       - name: Install dependencies
         run: npm ci
diff --git a/.github/workflows/release-extension.yml b/.github/workflows/release-extension.yml
index cb161beb..72cc4041 100644
--- a/.github/workflows/release-extension.yml
+++ b/.github/workflows/release-extension.yml
@@ -24,12 +24,10 @@ jobs:
         run: |
           git config user.name "web-scrobbler-bot"
           git config user.email "${{ secrets.BOT_EMAIL }}"
-      - name: Get Node.js version from .nvmrc
-        run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
       - name: Install dependencies
         run: npm ci
       - name: Release
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 30b63056..04cac65a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,16 +11,13 @@ jobs:
       - name: Checkout the repository
         uses: actions/checkout@v3
 
-      - name: Get Node.js version from .nvmrc
-        run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
-
       - name: Get the extension version from ref
         run: echo "EXT_VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
 
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
 
       - name: Install dependencies
         run: npm ci
diff --git a/package-lock.json b/package-lock.json
index f6e9fb84..dfdecae7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
       "license": "MIT",
       "dependencies": {
         "@suid/icons-material": "0.6.9",
-        "@web-scrobbler/metadata-filter": "3.0.1",
+        "@web-scrobbler/metadata-filter": "3.0.2",
         "blueimp-md5": "2.19.0",
         "stylelint": "13.13.1",
         "vite": "4.4.9",
@@ -22,30 +22,30 @@
         "@types/fs-extra": "11.0.1",
         "@types/imagemin": "8.0.1",
         "@types/imagemin-jpegtran": "5.0.1",
-        "@types/node": "20.5.0",
+        "@types/node": "20.5.6",
         "@types/react": "18.2.20",
         "@types/webextension-polyfill": "0.10.1",
-        "@typescript-eslint/eslint-plugin": "6.3.0",
-        "archiver": "5.3.1",
+        "@typescript-eslint/eslint-plugin": "6.4.1",
+        "archiver": "6.0.0",
         "canvas": "2.11.2",
         "chokidar": "3.5.3",
-        "esbuild": "0.19.1",
+        "esbuild": "0.19.2",
         "eslint": "8.47.0",
         "eslint-config-web-scrobbler": "2.1.1",
-        "eslint-plugin-react": "7.33.1",
+        "eslint-plugin-react": "7.33.2",
         "eslint-plugin-solid": "0.12.1",
         "eslint-plugin-tsdoc": "0.2.17",
         "fs-extra": "11.1.1",
-        "glob": "10.3.3",
+        "glob": "10.3.4",
         "imagemin": "8.0.1",
         "imagemin-jpegtran": "7.0.0",
         "imagemin-pngquant": "9.0.2",
-        "prettier": "3.0.1",
+        "prettier": "3.0.2",
         "prettier-config-web-scrobbler": "0.1.0",
         "remark": "14.0.3",
         "remark-cli": "11.0.0",
         "remark-preset-lint-recommended": "6.1.3",
-        "sass": "1.65.1",
+        "sass": "1.66.1",
         "solid-js": "1.7.11",
         "stylelint-config-web-scrobbler": "0.2.0",
         "tsx": "3.12.7",
@@ -53,7 +53,7 @@
         "vite-plugin-conditional-compiler": "0.1.1",
         "vite-plugin-solid": "2.7.0",
         "vite-plugin-static-copy": "0.17.0",
-        "vitest": "0.34.1"
+        "vitest": "0.34.2"
       },
       "engines": {
         "node": ">=18.0.0"
@@ -1073,9 +1073,9 @@
       }
     },
     "node_modules/@esbuild/android-arm": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.1.tgz",
-      "integrity": "sha512-yjTucwcOua52z14RL30JMwmCdylsQ5WrErGkAb6VL0MWPbnwJyLejydaRcUqkPO6g0MowlzavdxrR7AcfCW+yA==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz",
+      "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==",
       "cpu": [
         "arm"
       ],
@@ -1089,9 +1089,9 @@
       }
     },
     "node_modules/@esbuild/android-arm64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.1.tgz",
-      "integrity": "sha512-CqhrKvDSt76I0so/5afqgKrMv41FjbfUKFrcZddMnrZKqJU70I1MWLVJrImJuYMaY4Yb9rn4UKfF7oZ0BOleVw==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz",
+      "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==",
       "cpu": [
         "arm64"
       ],
@@ -1105,9 +1105,9 @@
       }
     },
     "node_modules/@esbuild/android-x64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.1.tgz",
-      "integrity": "sha512-VA29h01MrPkymIL1bFtvL2L4WPogiMGW2N/M+vXZHHOv6LgA9vjzVskTt0v5LjeCjx1PFDcR0ASKy8Y7Gm+iIA==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz",
+      "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==",
       "cpu": [
         "x64"
       ],
@@ -1121,9 +1121,9 @@
       }
     },
     "node_modules/@esbuild/darwin-arm64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.1.tgz",
-      "integrity": "sha512-Be4Cf6WDH7QkLHEpfzQOlBOFdqmqYTSqw2yG3SVmzB3++wy3K7wiNGedezL+q6Jb4weqT9tchO5kkLDC08Jnzg==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz",
+      "integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==",
       "cpu": [
         "arm64"
       ],
@@ -1137,9 +1137,9 @@
       }
     },
     "node_modules/@esbuild/darwin-x64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.1.tgz",
-      "integrity": "sha512-SewtenJi6zCEfZRSUchb+LgJ/IQw8QvnKECPu/qHII1fLQKnVPUVR+VH2IuS03DD9WWnAi3yfOvBNwtrp3WXtg==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz",
+      "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==",
       "cpu": [
         "x64"
       ],
@@ -1153,9 +1153,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.1.tgz",
-      "integrity": "sha512-TadKO0AaTDAPV2RyGZQ0AaiDTVYg7RsgNaA6OJjXXmoLbTs++NwHtzAmVFBq8Q/P9A11wgkv36HeyAYhWHbW1w==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz",
+      "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==",
       "cpu": [
         "arm64"
       ],
@@ -1169,9 +1169,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-x64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.1.tgz",
-      "integrity": "sha512-DrFMGLF0/aAcZgwhtZr1cby7aHlalpFjLCe5CiI8mm0Kqhhc8gyNZKreaZzvir8CQe0H17p9xx6M9ben5R3r0g==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz",
+      "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==",
       "cpu": [
         "x64"
       ],
@@ -1185,9 +1185,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.1.tgz",
-      "integrity": "sha512-lCWDVPpQO/Dt5MEqctKujgtUVmwQx7J2Q83EqX/9BejN7BIX4fGJ0QKMiIyy21PFh+/64ArN+Ovh1tzYkTt2wg==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz",
+      "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==",
       "cpu": [
         "arm"
       ],
@@ -1201,9 +1201,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.1.tgz",
-      "integrity": "sha512-6ku/R2EzsdjyBaqQn+xGOPbv+BBYBXQYzlA04/46YQLmXkdApi0GYyUwiCXYBxm578iyywzGmM0rep1/q8tuFQ==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz",
+      "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==",
       "cpu": [
         "arm64"
       ],
@@ -1217,9 +1217,9 @@
       }
     },
     "node_modules/@esbuild/linux-ia32": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.1.tgz",
-      "integrity": "sha512-8AKFBk9v/zBDsADvK/0BWZUxkjEc0QDwO8rvbHJKqAZx6DF/VSeBxTRmqWeecrJmx+n3kemEwML9z0eD9IHweQ==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz",
+      "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==",
       "cpu": [
         "ia32"
       ],
@@ -1233,9 +1233,9 @@
       }
     },
     "node_modules/@esbuild/linux-loong64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.1.tgz",
-      "integrity": "sha512-6mOS5CxTGD8qOymp2y4KoM4ir+/REgjdJQFYpwP+WqjrWBo+PUevDGeHHjzCdw/R19PkFqS1bRzv8cTCmB/5kA==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz",
+      "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==",
       "cpu": [
         "loong64"
       ],
@@ -1249,9 +1249,9 @@
       }
     },
     "node_modules/@esbuild/linux-mips64el": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.1.tgz",
-      "integrity": "sha512-Bzmv6rRMzR4ErG2k/jwfj5jKNzVMVEI1tThuirFdAoE+duUv+jlDnlwxsN3s1eqMzADTOV2sSIcUUOfgv++Dgg==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz",
+      "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==",
       "cpu": [
         "mips64el"
       ],
@@ -1265,9 +1265,9 @@
       }
     },
     "node_modules/@esbuild/linux-ppc64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.1.tgz",
-      "integrity": "sha512-mPOxA7bd3nmx8TkuO/9M/tE0fnvmuX0wlpwnTL6DPLgkb/Z/KkupexSIw4cLfznn/fPzD89y17VWBjlVNyrpCQ==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz",
+      "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==",
       "cpu": [
         "ppc64"
       ],
@@ -1281,9 +1281,9 @@
       }
     },
     "node_modules/@esbuild/linux-riscv64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.1.tgz",
-      "integrity": "sha512-znYb2Mhe9xKIDeIYuTD6vCcUltvYzRT5Yq6sVcdhPrGu8DRdsNZS04B2tSeM+j7T03jL4yY+7/G/jxSJJ9LX2A==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz",
+      "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==",
       "cpu": [
         "riscv64"
       ],
@@ -1297,9 +1297,9 @@
       }
     },
     "node_modules/@esbuild/linux-s390x": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.1.tgz",
-      "integrity": "sha512-BBIE32cyqAYhMOQ42/jnecoF5P/S5lMob2vXSUiFpD3xCFbXOFkjP1OjfFKnalSO9+B5emvPTQFfNQXuLeVGEw==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz",
+      "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==",
       "cpu": [
         "s390x"
       ],
@@ -1313,9 +1313,9 @@
       }
     },
     "node_modules/@esbuild/linux-x64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.1.tgz",
-      "integrity": "sha512-PoCvKdHTIbnHmVJ5OEdewGMSw40HDFRTrC/imwh8vrp695RbSUpOqBqNBT45neK0FQleGFbSE/A9X6HlXPDhqA==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz",
+      "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==",
       "cpu": [
         "x64"
       ],
@@ -1329,9 +1329,9 @@
       }
     },
     "node_modules/@esbuild/netbsd-x64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.1.tgz",
-      "integrity": "sha512-4OrGMPorHCq9h52VLtyyyAmPjC2ZlANx54VDYyCrqXUOi+k0qxnPKXKKprVES67w2mE7TZJx9qZmT+jHeiZbHQ==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz",
+      "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==",
       "cpu": [
         "x64"
       ],
@@ -1345,9 +1345,9 @@
       }
     },
     "node_modules/@esbuild/openbsd-x64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.1.tgz",
-      "integrity": "sha512-3a7ZYMjBC4P3FKdTmUZHJw7Mhzp71m+iSFFhX1PnLZ03qmyaB2K+vJZCk4PjRjAvm5lSupJQQtM/AFMyLgKlxQ==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz",
+      "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==",
       "cpu": [
         "x64"
       ],
@@ -1361,9 +1361,9 @@
       }
     },
     "node_modules/@esbuild/sunos-x64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.1.tgz",
-      "integrity": "sha512-29yWBN5XfEjXT8yoeVb8cXfN1jAQLB+uskog1vBMhFR+YWOYvsrwPnh4hspETC/JnF95J+iETrvxgOUlICTWIw==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz",
+      "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==",
       "cpu": [
         "x64"
       ],
@@ -1377,9 +1377,9 @@
       }
     },
     "node_modules/@esbuild/win32-arm64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.1.tgz",
-      "integrity": "sha512-9Hb/WUXgyXlL55w3iNVyLkN9gq9x+agv3kk80foWbfpOwe7Qw4Vx6JGB+XQdsIfvvP1kShVQPIvBgVj0TxLlVw==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz",
+      "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==",
       "cpu": [
         "arm64"
       ],
@@ -1393,9 +1393,9 @@
       }
     },
     "node_modules/@esbuild/win32-ia32": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.1.tgz",
-      "integrity": "sha512-VGdtEcXX/f01NgoM8emDnpdOyrZCQ7VTwLv89MOl3mvJ5fbCOBMNCa8t7RZS4lf12RS87qOuJFX7Bh9aLTbSxg==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz",
+      "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==",
       "cpu": [
         "ia32"
       ],
@@ -1409,9 +1409,9 @@
       }
     },
     "node_modules/@esbuild/win32-x64": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.1.tgz",
-      "integrity": "sha512-H6u8OHmJkKJubLbukVOyi9yA5lzK8VE4TFEkZj2vgusTUPvFeMQ8YnWviVc9F6PuKS6ZIpOvi2/sfiW8tQZQ2g==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz",
+      "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==",
       "cpu": [
         "x64"
       ],
@@ -2347,9 +2347,9 @@
       "dev": true
     },
     "node_modules/@types/node": {
-      "version": "20.5.0",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.0.tgz",
-      "integrity": "sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==",
+      "version": "20.5.6",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.6.tgz",
+      "integrity": "sha512-Gi5wRGPbbyOTX+4Y2iULQ27oUPrefaB0PxGQJnfyWN3kvEDGM3mIB5M/gQLmitZf7A9FmLeaqxD3L1CXpm3VKQ==",
       "devOptional": true
     },
     "node_modules/@types/normalize-package-data": {
@@ -2424,21 +2424,20 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.3.0.tgz",
-      "integrity": "sha512-IZYjYZ0ifGSLZbwMqIip/nOamFiWJ9AH+T/GYNZBWkVcyNQOFGtSMoWV7RvY4poYCMZ/4lHzNl796WOSNxmk8A==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.1.tgz",
+      "integrity": "sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "6.3.0",
-        "@typescript-eslint/type-utils": "6.3.0",
-        "@typescript-eslint/utils": "6.3.0",
-        "@typescript-eslint/visitor-keys": "6.3.0",
+        "@typescript-eslint/scope-manager": "6.4.1",
+        "@typescript-eslint/type-utils": "6.4.1",
+        "@typescript-eslint/utils": "6.4.1",
+        "@typescript-eslint/visitor-keys": "6.4.1",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.2.4",
         "natural-compare": "^1.4.0",
-        "natural-compare-lite": "^1.4.0",
         "semver": "^7.5.4",
         "ts-api-utils": "^1.0.1"
       },
@@ -2460,13 +2459,13 @@
       }
     },
     "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.3.0.tgz",
-      "integrity": "sha512-WlNFgBEuGu74ahrXzgefiz/QlVb+qg8KDTpknKwR7hMH+lQygWyx0CQFoUmMn1zDkQjTBBIn75IxtWss77iBIQ==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.1.tgz",
+      "integrity": "sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.3.0",
-        "@typescript-eslint/visitor-keys": "6.3.0"
+        "@typescript-eslint/types": "6.4.1",
+        "@typescript-eslint/visitor-keys": "6.4.1"
       },
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -2476,18 +2475,58 @@
         "url": "https://opencollective.com/typescript-eslint"
       }
     },
+    "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.1.tgz",
+      "integrity": "sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg==",
+      "dev": true,
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.1.tgz",
+      "integrity": "sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "6.4.1",
+        "@typescript-eslint/visitor-keys": "6.4.1",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.3.0.tgz",
-      "integrity": "sha512-hLLg3BZE07XHnpzglNBG8P/IXq/ZVXraEbgY7FM0Cnc1ehM8RMdn9mat3LubJ3KBeYXXPxV1nugWbQPjGeJk6Q==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.1.tgz",
+      "integrity": "sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.12",
         "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "6.3.0",
-        "@typescript-eslint/types": "6.3.0",
-        "@typescript-eslint/typescript-estree": "6.3.0",
+        "@typescript-eslint/scope-manager": "6.4.1",
+        "@typescript-eslint/types": "6.4.1",
+        "@typescript-eslint/typescript-estree": "6.4.1",
         "semver": "^7.5.4"
       },
       "engines": {
@@ -2502,12 +2541,12 @@
       }
     },
     "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.3.0.tgz",
-      "integrity": "sha512-kEhRRj7HnvaSjux1J9+7dBen15CdWmDnwrpyiHsFX6Qx2iW5LOBUgNefOFeh2PjWPlNwN8TOn6+4eBU3J/gupw==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.1.tgz",
+      "integrity": "sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.3.0",
+        "@typescript-eslint/types": "6.4.1",
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
@@ -2614,13 +2653,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.3.0.tgz",
-      "integrity": "sha512-7Oj+1ox1T2Yc8PKpBvOKWhoI/4rWFd1j7FA/rPE0lbBPXTKjdbtC+7Ev0SeBjEKkIhKWVeZSP+mR7y1Db1CdfQ==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.1.tgz",
+      "integrity": "sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "6.3.0",
-        "@typescript-eslint/utils": "6.3.0",
+        "@typescript-eslint/typescript-estree": "6.4.1",
+        "@typescript-eslint/utils": "6.4.1",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.0.1"
       },
@@ -2641,13 +2680,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.3.0.tgz",
-      "integrity": "sha512-WlNFgBEuGu74ahrXzgefiz/QlVb+qg8KDTpknKwR7hMH+lQygWyx0CQFoUmMn1zDkQjTBBIn75IxtWss77iBIQ==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.1.tgz",
+      "integrity": "sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.3.0",
-        "@typescript-eslint/visitor-keys": "6.3.0"
+        "@typescript-eslint/types": "6.4.1",
+        "@typescript-eslint/visitor-keys": "6.4.1"
       },
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -2657,18 +2696,58 @@
         "url": "https://opencollective.com/typescript-eslint"
       }
     },
+    "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.1.tgz",
+      "integrity": "sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg==",
+      "dev": true,
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.1.tgz",
+      "integrity": "sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "6.4.1",
+        "@typescript-eslint/visitor-keys": "6.4.1",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.3.0.tgz",
-      "integrity": "sha512-hLLg3BZE07XHnpzglNBG8P/IXq/ZVXraEbgY7FM0Cnc1ehM8RMdn9mat3LubJ3KBeYXXPxV1nugWbQPjGeJk6Q==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.1.tgz",
+      "integrity": "sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.12",
         "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "6.3.0",
-        "@typescript-eslint/types": "6.3.0",
-        "@typescript-eslint/typescript-estree": "6.3.0",
+        "@typescript-eslint/scope-manager": "6.4.1",
+        "@typescript-eslint/types": "6.4.1",
+        "@typescript-eslint/typescript-estree": "6.4.1",
         "semver": "^7.5.4"
       },
       "engines": {
@@ -2683,12 +2762,12 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.3.0.tgz",
-      "integrity": "sha512-kEhRRj7HnvaSjux1J9+7dBen15CdWmDnwrpyiHsFX6Qx2iW5LOBUgNefOFeh2PjWPlNwN8TOn6+4eBU3J/gupw==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.1.tgz",
+      "integrity": "sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.3.0",
+        "@typescript-eslint/types": "6.4.1",
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
@@ -2704,6 +2783,7 @@
       "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.3.0.tgz",
       "integrity": "sha512-K6TZOvfVyc7MO9j60MkRNWyFSf86IbOatTKGrpTQnzarDZPYPVy0oe3myTMq7VjhfsUAbNUW8I5s+2lZvtx1gg==",
       "dev": true,
+      "peer": true,
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
       },
@@ -2717,6 +2797,7 @@
       "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.3.0.tgz",
       "integrity": "sha512-Xh4NVDaC4eYKY4O3QGPuQNp5NxBAlEvNQYOqJquR2MePNxO11E5K3t5x4M4Mx53IZvtpW+mBxIT0s274fLUocg==",
       "dev": true,
+      "peer": true,
       "dependencies": {
         "@typescript-eslint/types": "6.3.0",
         "@typescript-eslint/visitor-keys": "6.3.0",
@@ -2744,6 +2825,7 @@
       "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.3.0.tgz",
       "integrity": "sha512-kEhRRj7HnvaSjux1J9+7dBen15CdWmDnwrpyiHsFX6Qx2iW5LOBUgNefOFeh2PjWPlNwN8TOn6+4eBU3J/gupw==",
       "dev": true,
+      "peer": true,
       "dependencies": {
         "@typescript-eslint/types": "6.3.0",
         "eslint-visitor-keys": "^3.4.1"
@@ -2853,13 +2935,13 @@
       }
     },
     "node_modules/@vitest/expect": {
-      "version": "0.34.1",
-      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.1.tgz",
-      "integrity": "sha512-q2CD8+XIsQ+tHwypnoCk8Mnv5e6afLFvinVGCq3/BOT4kQdVQmY6rRfyKkwcg635lbliLPqbunXZr+L1ssUWiQ==",
+      "version": "0.34.2",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.2.tgz",
+      "integrity": "sha512-EZm2dMNlLyIfDMha17QHSQcg2KjeAZaXd65fpPzXY5bvnfx10Lcaz3N55uEe8PhF+w4pw+hmrlHLLlRn9vkBJg==",
       "dev": true,
       "dependencies": {
-        "@vitest/spy": "0.34.1",
-        "@vitest/utils": "0.34.1",
+        "@vitest/spy": "0.34.2",
+        "@vitest/utils": "0.34.2",
         "chai": "^4.3.7"
       },
       "funding": {
@@ -2867,12 +2949,12 @@
       }
     },
     "node_modules/@vitest/runner": {
-      "version": "0.34.1",
-      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.1.tgz",
-      "integrity": "sha512-YfQMpYzDsYB7yqgmlxZ06NI4LurHWfrH7Wy3Pvf/z/vwUSgq1zLAb1lWcItCzQG+NVox+VvzlKQrYEXb47645g==",
+      "version": "0.34.2",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.2.tgz",
+      "integrity": "sha512-8ydGPACVX5tK3Dl0SUwxfdg02h+togDNeQX3iXVFYgzF5odxvaou7HnquALFZkyVuYskoaHUOqOyOLpOEj5XTA==",
       "dev": true,
       "dependencies": {
-        "@vitest/utils": "0.34.1",
+        "@vitest/utils": "0.34.2",
         "p-limit": "^4.0.0",
         "pathe": "^1.1.1"
       },
@@ -2908,9 +2990,9 @@
       }
     },
     "node_modules/@vitest/snapshot": {
-      "version": "0.34.1",
-      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.1.tgz",
-      "integrity": "sha512-0O9LfLU0114OqdF8lENlrLsnn024Tb1CsS9UwG0YMWY2oGTQfPtkW+B/7ieyv0X9R2Oijhi3caB1xgGgEgclSQ==",
+      "version": "0.34.2",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.2.tgz",
+      "integrity": "sha512-qhQ+xy3u4mwwLxltS4Pd4SR+XHv4EajiTPNY3jkIBLUApE6/ce72neJPSUQZ7bL3EBuKI+NhvzhGj3n5baRQUQ==",
       "dev": true,
       "dependencies": {
         "magic-string": "^0.30.1",
@@ -2922,9 +3004,9 @@
       }
     },
     "node_modules/@vitest/spy": {
-      "version": "0.34.1",
-      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.1.tgz",
-      "integrity": "sha512-UT4WcI3EAPUNO8n6y9QoEqynGGEPmmRxC+cLzneFFXpmacivjHZsNbiKD88KUScv5DCHVDgdBsLD7O7s1enFcQ==",
+      "version": "0.34.2",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.2.tgz",
+      "integrity": "sha512-yd4L9OhfH6l0Av7iK3sPb3MykhtcRN5c5K5vm1nTbuN7gYn+yvUVVsyvzpHrjqS7EWqn9WsPJb7+0c3iuY60tA==",
       "dev": true,
       "dependencies": {
         "tinyspy": "^2.1.1"
@@ -2934,9 +3016,9 @@
       }
     },
     "node_modules/@vitest/utils": {
-      "version": "0.34.1",
-      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.1.tgz",
-      "integrity": "sha512-/ql9dsFi4iuEbiNcjNHQWXBum7aL8pyhxvfnD9gNtbjR9fUKAjxhj4AA3yfLXg6gJpMGGecvtF8Au2G9y3q47Q==",
+      "version": "0.34.2",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.2.tgz",
+      "integrity": "sha512-Lzw+kAsTPubhoQDp1uVAOP6DhNia1GMDsI9jgB0yMn+/nDaPieYQ88lKqz/gGjSHL4zwOItvpehec9OY+rS73w==",
       "dev": true,
       "dependencies": {
         "diff-sequences": "^29.4.3",
@@ -2948,9 +3030,9 @@
       }
     },
     "node_modules/@web-scrobbler/metadata-filter": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/@web-scrobbler/metadata-filter/-/metadata-filter-3.0.1.tgz",
-      "integrity": "sha512-/E0X2tWOIHdZfDhJmAAqZyXyBO7/769ap1H0B7VXvixbRIV+/NCsxrWtDdPFH4hnlTwkE5yvQJg6d7KoyktHwQ==",
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@web-scrobbler/metadata-filter/-/metadata-filter-3.0.2.tgz",
+      "integrity": "sha512-rmsjHHLgvLRTF2PunBA7f+jSB/MwPczPU/xnkOwfeOWVOw9jdyHhr8AnLu20K+b702vtgTDb7wkDxMCDP0UohA==",
       "engines": {
         "node": ">=10.0.0"
       }
@@ -3105,21 +3187,21 @@
       }
     },
     "node_modules/archiver": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz",
-      "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.0.tgz",
+      "integrity": "sha512-EPGa+bYaxaMiCT8DCbEDqFz8IjeBSExrJzyUOJx2FBkFJ/OZzJuso3lMSk901M50gMqXxTQcumlGajOFlXhVhw==",
       "dev": true,
       "dependencies": {
-        "archiver-utils": "^2.1.0",
-        "async": "^3.2.3",
+        "archiver-utils": "^3.0.0",
+        "async": "^3.2.4",
         "buffer-crc32": "^0.2.1",
         "readable-stream": "^3.6.0",
-        "readdir-glob": "^1.0.0",
+        "readdir-glob": "^1.1.2",
         "tar-stream": "^2.2.0",
         "zip-stream": "^4.1.0"
       },
       "engines": {
-        "node": ">= 10"
+        "node": ">= 12.0.0"
       }
     },
     "node_modules/archiver-utils": {
@@ -3193,6 +3275,47 @@
         "safe-buffer": "~5.1.0"
       }
     },
+    "node_modules/archiver/node_modules/archiver-utils": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.3.tgz",
+      "integrity": "sha512-fXzpEZTKgBJMWy0eUT0/332CAQnJ27OJd7sGcvNZzxS2Yzg7iITivMhXOm+zUTO4vT8ZqlPCqiaLPmB8qWhWRA==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.1.4",
+        "graceful-fs": "^4.2.0",
+        "lazystream": "^1.0.0",
+        "lodash.defaults": "^4.2.0",
+        "lodash.difference": "^4.5.0",
+        "lodash.flatten": "^4.4.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.union": "^4.6.0",
+        "normalize-path": "^3.0.0",
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/archiver/node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/are-we-there-yet": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
@@ -3292,6 +3415,26 @@
         "get-intrinsic": "^1.1.3"
       }
     },
+    "node_modules/arraybuffer.prototype.slice": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz",
+      "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==",
+      "dev": true,
+      "dependencies": {
+        "array-buffer-byte-length": "^1.0.0",
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.2.0",
+        "get-intrinsic": "^1.2.1",
+        "is-array-buffer": "^3.0.2",
+        "is-shared-array-buffer": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/arrify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
@@ -3323,6 +3466,15 @@
       "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==",
       "dev": true
     },
+    "node_modules/asynciterator.prototype": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz",
+      "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==",
+      "dev": true,
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      }
+    },
     "node_modules/autoprefixer": {
       "version": "9.8.8",
       "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz",
@@ -4886,18 +5038,19 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.21.2",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
-      "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==",
+      "version": "1.22.1",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz",
+      "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==",
       "dev": true,
       "dependencies": {
         "array-buffer-byte-length": "^1.0.0",
+        "arraybuffer.prototype.slice": "^1.0.1",
         "available-typed-arrays": "^1.0.5",
         "call-bind": "^1.0.2",
         "es-set-tostringtag": "^2.0.1",
         "es-to-primitive": "^1.2.1",
         "function.prototype.name": "^1.1.5",
-        "get-intrinsic": "^1.2.0",
+        "get-intrinsic": "^1.2.1",
         "get-symbol-description": "^1.0.0",
         "globalthis": "^1.0.3",
         "gopd": "^1.0.1",
@@ -4917,14 +5070,18 @@
         "object-inspect": "^1.12.3",
         "object-keys": "^1.1.1",
         "object.assign": "^4.1.4",
-        "regexp.prototype.flags": "^1.4.3",
+        "regexp.prototype.flags": "^1.5.0",
+        "safe-array-concat": "^1.0.0",
         "safe-regex-test": "^1.0.0",
         "string.prototype.trim": "^1.2.7",
         "string.prototype.trimend": "^1.0.6",
         "string.prototype.trimstart": "^1.0.6",
+        "typed-array-buffer": "^1.0.0",
+        "typed-array-byte-length": "^1.0.0",
+        "typed-array-byte-offset": "^1.0.0",
         "typed-array-length": "^1.0.4",
         "unbox-primitive": "^1.0.2",
-        "which-typed-array": "^1.1.9"
+        "which-typed-array": "^1.1.10"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4933,6 +5090,28 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/es-iterator-helpers": {
+      "version": "1.0.13",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.13.tgz",
+      "integrity": "sha512-LK3VGwzvaPWobO8xzXXGRUOGw8Dcjyfk62CsY/wfHN75CwsJPbuypOYJxK6g5RyEL8YDjIWcl6jgd8foO6mmrA==",
+      "dev": true,
+      "dependencies": {
+        "asynciterator.prototype": "^1.0.0",
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.2.0",
+        "es-abstract": "^1.21.3",
+        "es-set-tostringtag": "^2.0.1",
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.2.1",
+        "globalthis": "^1.0.3",
+        "has-property-descriptors": "^1.0.0",
+        "has-proto": "^1.0.1",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.5",
+        "iterator.prototype": "^1.1.0",
+        "safe-array-concat": "^1.0.0"
+      }
+    },
     "node_modules/es-set-tostringtag": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
@@ -4974,9 +5153,9 @@
       }
     },
     "node_modules/esbuild": {
-      "version": "0.19.1",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.1.tgz",
-      "integrity": "sha512-IknHHwV4B/H4imOAu+416fuCvPfRjdncoyGi7eunhSvHuHkdNs50sLWan2LEG2Mym07TuW6gJUIyRS9G1miHEg==",
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz",
+      "integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==",
       "dev": true,
       "hasInstallScript": true,
       "bin": {
@@ -4986,28 +5165,28 @@
         "node": ">=12"
       },
       "optionalDependencies": {
-        "@esbuild/android-arm": "0.19.1",
-        "@esbuild/android-arm64": "0.19.1",
-        "@esbuild/android-x64": "0.19.1",
-        "@esbuild/darwin-arm64": "0.19.1",
-        "@esbuild/darwin-x64": "0.19.1",
-        "@esbuild/freebsd-arm64": "0.19.1",
-        "@esbuild/freebsd-x64": "0.19.1",
-        "@esbuild/linux-arm": "0.19.1",
-        "@esbuild/linux-arm64": "0.19.1",
-        "@esbuild/linux-ia32": "0.19.1",
-        "@esbuild/linux-loong64": "0.19.1",
-        "@esbuild/linux-mips64el": "0.19.1",
-        "@esbuild/linux-ppc64": "0.19.1",
-        "@esbuild/linux-riscv64": "0.19.1",
-        "@esbuild/linux-s390x": "0.19.1",
-        "@esbuild/linux-x64": "0.19.1",
-        "@esbuild/netbsd-x64": "0.19.1",
-        "@esbuild/openbsd-x64": "0.19.1",
-        "@esbuild/sunos-x64": "0.19.1",
-        "@esbuild/win32-arm64": "0.19.1",
-        "@esbuild/win32-ia32": "0.19.1",
-        "@esbuild/win32-x64": "0.19.1"
+        "@esbuild/android-arm": "0.19.2",
+        "@esbuild/android-arm64": "0.19.2",
+        "@esbuild/android-x64": "0.19.2",
+        "@esbuild/darwin-arm64": "0.19.2",
+        "@esbuild/darwin-x64": "0.19.2",
+        "@esbuild/freebsd-arm64": "0.19.2",
+        "@esbuild/freebsd-x64": "0.19.2",
+        "@esbuild/linux-arm": "0.19.2",
+        "@esbuild/linux-arm64": "0.19.2",
+        "@esbuild/linux-ia32": "0.19.2",
+        "@esbuild/linux-loong64": "0.19.2",
+        "@esbuild/linux-mips64el": "0.19.2",
+        "@esbuild/linux-ppc64": "0.19.2",
+        "@esbuild/linux-riscv64": "0.19.2",
+        "@esbuild/linux-s390x": "0.19.2",
+        "@esbuild/linux-x64": "0.19.2",
+        "@esbuild/netbsd-x64": "0.19.2",
+        "@esbuild/openbsd-x64": "0.19.2",
+        "@esbuild/sunos-x64": "0.19.2",
+        "@esbuild/win32-arm64": "0.19.2",
+        "@esbuild/win32-ia32": "0.19.2",
+        "@esbuild/win32-x64": "0.19.2"
       }
     },
     "node_modules/escalade": {
@@ -5124,15 +5303,16 @@
       }
     },
     "node_modules/eslint-plugin-react": {
-      "version": "7.33.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.1.tgz",
-      "integrity": "sha512-L093k0WAMvr6VhNwReB8VgOq5s2LesZmrpPdKz/kZElQDzqS7G7+DnKoqT+w4JwuiGeAhAvHO0fvy0Eyk4ejDA==",
+      "version": "7.33.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz",
+      "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==",
       "dev": true,
       "dependencies": {
         "array-includes": "^3.1.6",
         "array.prototype.flatmap": "^1.3.1",
         "array.prototype.tosorted": "^1.1.1",
         "doctrine": "^2.1.0",
+        "es-iterator-helpers": "^1.0.12",
         "estraverse": "^5.3.0",
         "jsx-ast-utils": "^2.4.1 || ^3.0.0",
         "minimatch": "^3.1.2",
@@ -5951,13 +6131,14 @@
       }
     },
     "node_modules/get-intrinsic": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
-      "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+      "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
       "dev": true,
       "dependencies": {
         "function-bind": "^1.1.1",
         "has": "^1.0.3",
+        "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3"
       },
       "funding": {
@@ -6020,9 +6201,9 @@
       }
     },
     "node_modules/glob": {
-      "version": "10.3.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz",
-      "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==",
+      "version": "10.3.4",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz",
+      "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==",
       "dev": true,
       "dependencies": {
         "foreground-child": "^3.1.0",
@@ -6771,6 +6952,21 @@
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
       "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
     },
+    "node_modules/is-async-function": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
+      "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==",
+      "dev": true,
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-bigint": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
@@ -6894,6 +7090,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-finalizationregistry": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz",
+      "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-finite": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
@@ -6914,6 +7122,21 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-generator-function": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+      "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+      "dev": true,
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-glob": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -6955,6 +7178,15 @@
         "node": ">=6"
       }
     },
+    "node_modules/is-map": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
+      "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-natural-number": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz",
@@ -7064,6 +7296,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-set": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
+      "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-shared-array-buffer": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
@@ -7156,6 +7397,15 @@
       "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==",
       "dev": true
     },
+    "node_modules/is-weakmap": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
+      "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-weakref": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
@@ -7168,6 +7418,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-weakset": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
+      "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-what": {
       "version": "4.1.8",
       "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz",
@@ -7204,6 +7467,19 @@
         "node": ">= 4"
       }
     },
+    "node_modules/iterator.prototype": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.0.tgz",
+      "integrity": "sha512-rjuhAk1AJ1fssphHD0IFV6TWL40CwRZ53FrztKx43yk2v6rguBYsY4Bj1VU4HmoMmKwZUlx7mfnhDf9cOp4YTw==",
+      "dev": true,
+      "dependencies": {
+        "define-properties": "^1.1.4",
+        "get-intrinsic": "^1.1.3",
+        "has-symbols": "^1.0.3",
+        "has-tostringtag": "^1.0.0",
+        "reflect.getprototypeof": "^1.0.3"
+      }
+    },
     "node_modules/jackspeak": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.0.3.tgz",
@@ -8528,12 +8804,6 @@
       "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
       "dev": true
     },
-    "node_modules/natural-compare-lite": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
-      "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
-      "dev": true
-    },
     "node_modules/nice-try": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -9493,9 +9763,9 @@
       }
     },
     "node_modules/prettier": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
-      "integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz",
+      "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==",
       "dev": true,
       "bin": {
         "prettier": "bin/prettier.cjs"
@@ -9826,15 +10096,35 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/reflect.getprototypeof": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.3.tgz",
+      "integrity": "sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "get-intrinsic": "^1.1.1",
+        "globalthis": "^1.0.3",
+        "which-builtin-type": "^1.1.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/regexp.prototype.flags": {
-      "version": "1.4.3",
-      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
-      "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
+      "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
       "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3",
-        "functions-have-names": "^1.2.2"
+        "define-properties": "^1.2.0",
+        "functions-have-names": "^1.2.3"
       },
       "engines": {
         "node": ">= 0.4"
@@ -10391,6 +10681,30 @@
         "node": ">=6"
       }
     },
+    "node_modules/safe-array-concat": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz",
+      "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.2.0",
+        "has-symbols": "^1.0.3",
+        "isarray": "^2.0.5"
+      },
+      "engines": {
+        "node": ">=0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/safe-array-concat/node_modules/isarray": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+      "dev": true
+    },
     "node_modules/safe-buffer": {
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -10425,9 +10739,9 @@
       }
     },
     "node_modules/sass": {
-      "version": "1.65.1",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.65.1.tgz",
-      "integrity": "sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA==",
+      "version": "1.66.1",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz",
+      "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==",
       "devOptional": true,
       "dependencies": {
         "chokidar": ">=3.0.0 <4.0.0",
@@ -11860,6 +12174,57 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/typed-array-buffer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz",
+      "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.2.1",
+        "is-typed-array": "^1.1.10"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/typed-array-byte-length": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz",
+      "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "has-proto": "^1.0.1",
+        "is-typed-array": "^1.1.10"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/typed-array-byte-offset": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz",
+      "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==",
+      "dev": true,
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "has-proto": "^1.0.1",
+        "is-typed-array": "^1.1.10"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/typed-array-length": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
@@ -12647,9 +13012,9 @@
       }
     },
     "node_modules/vite-node": {
-      "version": "0.34.1",
-      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.1.tgz",
-      "integrity": "sha512-odAZAL9xFMuAg8aWd7nSPT+hU8u2r9gU3LRm9QKjxBEF2rRdWpMuqkrkjvyVQEdNFiBctqr2Gg4uJYizm5Le6w==",
+      "version": "0.34.2",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.2.tgz",
+      "integrity": "sha512-JtW249Zm3FB+F7pQfH56uWSdlltCo1IOkZW5oHBzeQo0iX4jtC7o1t9aILMGd9kVekXBP2lfJBEQt9rBh07ebA==",
       "dev": true,
       "dependencies": {
         "cac": "^6.7.14",
@@ -13140,19 +13505,19 @@
       }
     },
     "node_modules/vitest": {
-      "version": "0.34.1",
-      "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.1.tgz",
-      "integrity": "sha512-G1PzuBEq9A75XSU88yO5G4vPT20UovbC/2osB2KEuV/FisSIIsw7m5y2xMdB7RsAGHAfg2lPmp2qKr3KWliVlQ==",
+      "version": "0.34.2",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.2.tgz",
+      "integrity": "sha512-WgaIvBbjsSYMq/oiMlXUI7KflELmzM43BEvkdC/8b5CAod4ryAiY2z8uR6Crbi5Pjnu5oOmhKa9sy7uk6paBxQ==",
       "dev": true,
       "dependencies": {
         "@types/chai": "^4.3.5",
         "@types/chai-subset": "^1.3.3",
         "@types/node": "*",
-        "@vitest/expect": "0.34.1",
-        "@vitest/runner": "0.34.1",
-        "@vitest/snapshot": "0.34.1",
-        "@vitest/spy": "0.34.1",
-        "@vitest/utils": "0.34.1",
+        "@vitest/expect": "0.34.2",
+        "@vitest/runner": "0.34.2",
+        "@vitest/snapshot": "0.34.2",
+        "@vitest/spy": "0.34.2",
+        "@vitest/utils": "0.34.2",
         "acorn": "^8.9.0",
         "acorn-walk": "^8.2.0",
         "cac": "^6.7.14",
@@ -13167,7 +13532,7 @@
         "tinybench": "^2.5.0",
         "tinypool": "^0.7.0",
         "vite": "^3.0.0 || ^4.0.0",
-        "vite-node": "0.34.1",
+        "vite-node": "0.34.2",
         "why-is-node-running": "^2.2.2"
       },
       "bin": {
@@ -13319,18 +13684,64 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/which-builtin-type": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz",
+      "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==",
+      "dev": true,
+      "dependencies": {
+        "function.prototype.name": "^1.1.5",
+        "has-tostringtag": "^1.0.0",
+        "is-async-function": "^2.0.0",
+        "is-date-object": "^1.0.5",
+        "is-finalizationregistry": "^1.0.2",
+        "is-generator-function": "^1.0.10",
+        "is-regex": "^1.1.4",
+        "is-weakref": "^1.0.2",
+        "isarray": "^2.0.5",
+        "which-boxed-primitive": "^1.0.2",
+        "which-collection": "^1.0.1",
+        "which-typed-array": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/which-builtin-type/node_modules/isarray": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+      "dev": true
+    },
+    "node_modules/which-collection": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
+      "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
+      "dev": true,
+      "dependencies": {
+        "is-map": "^2.0.1",
+        "is-set": "^2.0.1",
+        "is-weakmap": "^2.0.1",
+        "is-weakset": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/which-typed-array": {
-      "version": "1.1.9",
-      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
-      "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
+      "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
       "dev": true,
       "dependencies": {
         "available-typed-arrays": "^1.0.5",
         "call-bind": "^1.0.2",
         "for-each": "^0.3.3",
         "gopd": "^1.0.1",
-        "has-tostringtag": "^1.0.0",
-        "is-typed-array": "^1.1.10"
+        "has-tostringtag": "^1.0.0"
       },
       "engines": {
         "node": ">= 0.4"
diff --git a/package.json b/package.json
index 622542ba..3c653cfa 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,7 @@
   "homepage": "https://web-scrobbler.com/",
   "dependencies": {
     "@suid/icons-material": "0.6.9",
-    "@web-scrobbler/metadata-filter": "3.0.1",
+    "@web-scrobbler/metadata-filter": "3.0.2",
     "blueimp-md5": "2.19.0",
     "stylelint": "13.13.1",
     "vite": "4.4.9",
@@ -39,30 +39,30 @@
     "@types/fs-extra": "11.0.1",
     "@types/imagemin": "8.0.1",
     "@types/imagemin-jpegtran": "5.0.1",
-    "@types/node": "20.5.0",
+    "@types/node": "20.5.6",
     "@types/react": "18.2.20",
     "@types/webextension-polyfill": "0.10.1",
-    "@typescript-eslint/eslint-plugin": "6.3.0",
-    "archiver": "5.3.1",
+    "@typescript-eslint/eslint-plugin": "6.4.1",
+    "archiver": "6.0.0",
     "canvas": "2.11.2",
     "chokidar": "3.5.3",
-    "esbuild": "0.19.1",
+    "esbuild": "0.19.2",
     "eslint": "8.47.0",
     "eslint-config-web-scrobbler": "2.1.1",
-    "eslint-plugin-react": "7.33.1",
+    "eslint-plugin-react": "7.33.2",
     "eslint-plugin-solid": "0.12.1",
     "eslint-plugin-tsdoc": "0.2.17",
     "fs-extra": "11.1.1",
-    "glob": "10.3.3",
+    "glob": "10.3.4",
     "imagemin": "8.0.1",
     "imagemin-jpegtran": "7.0.0",
     "imagemin-pngquant": "9.0.2",
-    "prettier": "3.0.1",
+    "prettier": "3.0.2",
     "prettier-config-web-scrobbler": "0.1.0",
     "remark": "14.0.3",
     "remark-cli": "11.0.0",
     "remark-preset-lint-recommended": "6.1.3",
-    "sass": "1.65.1",
+    "sass": "1.66.1",
     "solid-js": "1.7.11",
     "stylelint-config-web-scrobbler": "0.2.0",
     "tsx": "3.12.7",
@@ -70,7 +70,7 @@
     "vite-plugin-conditional-compiler": "0.1.1",
     "vite-plugin-solid": "2.7.0",
     "vite-plugin-static-copy": "0.17.0",
-    "vitest": "0.34.1"
+    "vitest": "0.34.2"
   },
   "engines": {
     "node": ">=18.0.0"
diff --git a/src/connectors/gensokyoradio-dom-inject.ts b/src/connectors/gensokyoradio-dom-inject.ts
new file mode 100644
index 00000000..838d2840
--- /dev/null
+++ b/src/connectors/gensokyoradio-dom-inject.ts
@@ -0,0 +1,31 @@
+/**
+ * This script runs in non-isolated environments (gensokyoradio.net) so that we
+ * have access to the global `audio` player.
+ */
+
+interface GensokyoWindow {
+	audio: HTMLAudioElement;
+}
+
+gensokyoSetupEventListener();
+
+function gensokyoSendUpdateEvent(type: string) {
+	window.postMessage(
+		{
+			sender: 'web-scrobbler',
+			type,
+			isPlaying: (window as unknown as GensokyoWindow).audio.paused
+				? false
+				: true,
+		},
+		'*',
+	);
+}
+
+function gensokyoSetupEventListener() {
+	const audio = (window as unknown as GensokyoWindow).audio;
+
+	for (const e of ['play', 'pause', 'ended', 'emptied', 'suspend']) {
+		audio.addEventListener(e, gensokyoSendUpdateEvent.bind(null, e));
+	}
+}
diff --git a/src/connectors/gensokyoradio.ts b/src/connectors/gensokyoradio.ts
new file mode 100644
index 00000000..fe6b1fa0
--- /dev/null
+++ b/src/connectors/gensokyoradio.ts
@@ -0,0 +1,34 @@
+export {};
+
+let isPlaying = false;
+
+Connector.playerSelector = '#heroMain';
+
+Connector.artistSelector = '#playerArtist';
+
+Connector.trackSelector = '#playerTitle';
+
+Connector.albumSelector = '#playerAlbum';
+
+Connector.albumArtistSelector = '#playerCircle';
+
+Connector.trackArtSelector = '#playerArt';
+
+Connector.timeInfoSelector = '#playerCounter';
+
+Connector.isPlaying = () => isPlaying;
+
+Connector.isTrackArtDefault = (trackArtUrl) => {
+	return (
+		!!trackArtUrl &&
+		/(gr-logo-placeholder|no-albumart)\.png$/i.test(trackArtUrl)
+	);
+};
+
+Connector.onScriptEvent = (event) => {
+	isPlaying = !!event.data.isPlaying;
+
+	Connector.onStateChanged();
+};
+
+Connector.injectScript('connectors/gensokyoradio-dom-inject.js');
diff --git a/src/connectors/siriusxm-player.ts b/src/connectors/siriusxm-player.ts
index b79484f1..171d6172 100644
--- a/src/connectors/siriusxm-player.ts
+++ b/src/connectors/siriusxm-player.ts
@@ -1,14 +1,19 @@
 export {};
 
 /*
- * On some "decade" stations SiriusXM appends the last two digits
+ * Station Meta Filter
+ * - On some "decade" stations SiriusXM appends the last two digits
  * of the release year to the song title like this: Where Is The Love (03).
+ * - On The Covers Channel, it adds Heroes (David Bowie Cover) to the track
+ * - A lot of stations seem to employ adding EXCLUSIVE to the track name, so that is now removed
  *
- * This filter removes such suffixes.
+ * Filter Station Meta
+ * - There is now a filter for much of the station meta on SiriusXM. Removes social media handles, URLs, shownames
  */
 
-const filter = MetadataFilter.createFilter({ track: removeYear });
-const removeYearRe = /\s\(\d{2}\)\s?$/g;
+const filter = MetadataFilter.createFilter({
+	track: [removeYear, removeCover, removeExclusive],
+});
 
 Connector.playerSelector = ".sxm-player-controls";
 Connector.artistSelector = ".sxm-player-controls .artist-name";
@@ -18,7 +23,7 @@ Connector.isPlaying = () => {
 	return (
 		Util.getAttrFromSelectors(
 			".sxm-player-controls .play-pause-btn",
-			"title"
+			"title",
 		) === "Pause"
 	);
 };
@@ -46,12 +51,20 @@ Connector.isScrobblingAllowed = () => {
 	];
 
 	return !filteredTerms.some(
-		(term) => artist?.includes(term) || track?.includes(term)
+		(term) => artist?.includes(term) || track?.includes(term),
 	);
 };
 
 Connector.applyFilter(filter);
 
+function removeExclusive(track: string) {
+	return track.replace(/\sEXCLUSIVE/g, "");
+}
+
+function removeCover(track: string) {
+	return track.replace(/\([^)]*\b(?:cover|Cover)\b[^)]*\)/g, "");
+}
+
 function removeYear(track: string) {
-	return track.replace(removeYearRe, "");
+	return track.replace(/\s\(\d{2}\)\s?$/g, "");
 }
diff --git a/src/connectors/soundcloud.ts b/src/connectors/soundcloud.ts
index ecae120b..998de6f9 100644
--- a/src/connectors/soundcloud.ts
+++ b/src/connectors/soundcloud.ts
@@ -43,11 +43,12 @@ Connector.getRemainingTime = () => {
 Connector.isPlaying = () => Util.hasElementClass('.playControl', 'playing');
 
 Connector.getUniqueID = () => {
-	return (
+	const url = new URL((
 		document.querySelector(
 			'.playbackSoundBadge__titleLink'
 		) as HTMLAnchorElement
-	).href;
+	).href);
+	return url.origin + url.pathname;
 };
 
 Connector.getOriginUrl = () => {
diff --git a/src/connectors/stationhead.ts b/src/connectors/stationhead.ts
new file mode 100644
index 00000000..3542b33d
--- /dev/null
+++ b/src/connectors/stationhead.ts
@@ -0,0 +1,10 @@
+export {};
+
+const playerSelector = 
+	'#root>div>div:nth-child(2)>div>div>div>div>div:nth-child(2)>div:nth-child(2)>div';
+
+Connector.useMediaSessionApi();
+
+Connector.playerSelector = playerSelector;
+Connector.trackSelector = `${playerSelector} p`;
+Connector.artistSelector = `${playerSelector}>div>div>div p`;
diff --git a/src/core/background/main.ts b/src/core/background/main.ts
index 8ac71b50..04d61c32 100644
--- a/src/core/background/main.ts
+++ b/src/core/background/main.ts
@@ -29,6 +29,7 @@ import {
 import { CloneableSong } from '@/core/object/song';
 import {
 	clearNowPlaying,
+	showAuthNotification,
 	showNowPlaying,
 	showSongNotRecognized,
 } from '@/util/notifications';
@@ -36,6 +37,7 @@ import ClonedSong from '@/core/object/cloned-song';
 import { openTab } from '@/util/util-browser';
 import { setRegexDefaults } from '@/util/regex';
 import { getSongInfo, scrobble, sendNowPlaying, toggleLove } from './scrobble';
+import scrobbleService from '../object/scrobble-service';
 
 const disabledTabs = BrowserStorage.getStorage(BrowserStorage.DISABLED_TABS);
 
@@ -118,7 +120,7 @@ async function onTabRemoved(tabId: number) {
  * @param activeInfo - Information about the switch of tabs
  */
 async function onTabActivated(
-	activeInfo: browser.Tabs.OnActivatedActiveInfoType
+	activeInfo: browser.Tabs.OnActivatedActiveInfoType,
 ) {
 	await updateTabList(activeInfo.tabId, true);
 }
@@ -134,7 +136,7 @@ async function onTabActivated(
 async function onTabUpdated(
 	tabId: number,
 	changeInfo: browser.Tabs.OnUpdatedChangeInfoType,
-	tab?: browser.Tabs.Tab
+	tab?: browser.Tabs.Tab,
 ) {
 	if (tab?.active && changeInfo.status === 'complete') {
 		await updateTabList(tabId, false);
@@ -161,7 +163,7 @@ async function updateTabList(tabId: number, activated: boolean) {
 			newTabs = [curTab, ...newTabs];
 		} else {
 			newTabs = newTabs.map((active) =>
-				active.tabId === tabId ? curTab : active
+				active.tabId === tabId ? curTab : active,
 			);
 		}
 	}
@@ -182,7 +184,7 @@ async function updateTabList(tabId: number, activated: boolean) {
  */
 async function updateTab(
 	tabId: number | undefined,
-	fn: (tab: ManagerTab) => ManagerTab
+	fn: (tab: ManagerTab) => ManagerTab,
 ): Promise<void> {
 	if (!tabId) {
 		throw new Error('No tabid given');
@@ -250,7 +252,7 @@ async function updateMode(tabId: number | undefined, mode: ControllerModeStr) {
  */
 async function updateState(
 	tabId: number | undefined,
-	song: CloneableSong | null
+	song: CloneableSong | null,
 ) {
 	await updateTab(tabId, (oldTab) => ({
 		tabId: oldTab.tabId,
@@ -308,7 +310,7 @@ setupBackgroundListeners(
 				payload.connector,
 				() => {
 					openTab(sender.tab?.id ?? -1);
-				}
+				},
 			);
 		},
 	}),
@@ -320,7 +322,7 @@ setupBackgroundListeners(
 		type: 'setNowPlaying',
 		fn: (payload, sender) => {
 			return sendNowPlaying(
-				new ClonedSong(payload.song, sender.tab?.id ?? -1)
+				new ClonedSong(payload.song, sender.tab?.id ?? -1),
 			);
 		},
 	}),
@@ -342,7 +344,7 @@ setupBackgroundListeners(
 		type: 'getSongInfo',
 		fn: (payload, sender) => {
 			return getSongInfo(
-				new ClonedSong(payload.song, sender.tab?.id ?? -1)
+				new ClonedSong(payload.song, sender.tab?.id ?? -1),
 			);
 		},
 	}),
@@ -355,7 +357,7 @@ setupBackgroundListeners(
 		fn: (payload, sender) => {
 			return toggleLove(
 				new ClonedSong(payload.song, sender.tab?.id ?? -1),
-				payload.isLoved
+				payload.isLoved,
 			);
 		},
 	}),
@@ -381,7 +383,7 @@ setupBackgroundListeners(
 				payload.connector,
 				() => {
 					openTab(sender.tab?.id ?? -1);
-				}
+				},
 			);
 		},
 	}),
@@ -398,9 +400,38 @@ setupBackgroundListeners(
 				browserPreferredTheme: payload,
 			});
 		},
-	})
+	}),
 );
 
+/**
+ * Replace the extension version stored in local storage by current one.
+ */
+async function updateVersionInStorage() {
+	const storage = BrowserStorage.getStorage(BrowserStorage.CORE);
+	let data = await storage.get();
+	if (!data) {
+		data = {
+			appVersion: '',
+		};
+	}
+
+	data.appVersion = browser.runtime.getManifest().version;
+	await storage.set(data);
+
+	storage.debugLog();
+}
+
+/**
+ * Ask a scrobble service to bind scrobblers.
+ *
+ * @returns true if at least one scrobbler is registered;
+ *          false if no scrobblers are registered
+ */
+async function bindScrobblers() {
+	const boundScrobblers = await scrobbleService.bindAllScrobblers();
+	return boundScrobblers.length > 0;
+}
+
 /**
  * Sets up the starting state of the extension on browser startup/extension install.
  * Storage is used instead of variables, as with Manifest V3 service workers, script state cannot be guaranteed.
@@ -411,6 +442,13 @@ function startupFunc() {
 	disabledTabs.set({});
 
 	setRegexDefaults();
+	updateVersionInStorage();
+	bindScrobblers().then((bound) => {
+		if (!bound) {
+			console.warn('No scrobblers are bound');
+			showAuthNotification();
+		}
+	});
 
 	browser.contextMenus?.create({
 		id: contextMenus.ENABLE_CONNECTOR,
diff --git a/src/core/connectors.ts b/src/core/connectors.ts
index 41f919ae..b90bb4bf 100644
--- a/src/core/connectors.ts
+++ b/src/core/connectors.ts
@@ -1926,7 +1926,7 @@ export default <ConnectorMeta[]>[
 	},
 	{
 		label: 'Beatbump',
-		matches: ['*://beatbump.ml/*'],
+		matches: ['*://beatbump.io/*'],
 		js: 'beatbump.js',
 		id: 'beatbump',
 	},
@@ -2112,4 +2112,16 @@ export default <ConnectorMeta[]>[
 		js: 'trackerhub.js',
 		id: 'trackerhub',
 	},
+	{
+		label: 'Gensokyo Radio',
+		matches: ['*://gensokyoradio.net/*'],
+		js: 'gensokyoradio.js',
+		id: 'gensokyoradio',
+	},
+	{
+		label: 'Stationhead',
+		matches: ['*://*stationhead.com/*'],
+		js: 'stationhead.js',
+		id: 'stationhead',
+	},
 ];
diff --git a/src/ui/options/main.tsx b/src/ui/options/main.tsx
index 9b477304..9c0d4eee 100644
--- a/src/ui/options/main.tsx
+++ b/src/ui/options/main.tsx
@@ -12,6 +12,8 @@ import {
 	ModalType,
 	NavigatorNavigationButton,
 	aboutItem,
+	accountItem,
+	connectorOverrideOptionsItem,
 	settings,
 	showSomeLoveItem,
 } from './components/navigator';
@@ -26,11 +28,26 @@ function contextMenuQuery() {
 	return window.matchMedia('(max-width: 700px)').matches;
 }
 
-// Show some love as default except safari, where we don't want it shown because apple
-let defaultSetting = aboutItem;
-// #v-ifndef VITE_SAFARI
-defaultSetting = showSomeLoveItem;
-// #v-endif
+function getDefaultSetting(): NavigatorNavigationButton {
+	// if a page override is selected, return that page
+	const pageTitle = new URLSearchParams(window.location.search).get('p');
+	switch (pageTitle) {
+		case 'accounts':
+			return accountItem;
+		case 'connectors':
+			return connectorOverrideOptionsItem;
+	}
+
+	// in non-safari, go to show some love page
+	// #v-ifndef VITE_SAFARI
+	return showSomeLoveItem;
+	// #v-endif
+
+	// We actually reach this in safari, return about page
+	return aboutItem;
+}
+
+const defaultSetting = getDefaultSetting();
 
 /**
  * Preferences component, with a sidebar and several different options and info pages
@@ -59,7 +76,7 @@ function Options() {
 	onCleanup(() => document.removeEventListener('click', onclick));
 
 	const [shouldShowContextMenu, setShouldShowContextMenu] = createSignal(
-		contextMenuQuery()
+		contextMenuQuery(),
 	);
 	const resizeListener = () => setShouldShowContextMenu(contextMenuQuery());
 	window.addEventListener('resize', resizeListener);
diff --git a/src/ui/popup/disabled.tsx b/src/ui/popup/disabled.tsx
index 6a03ad8a..81ff9513 100644
--- a/src/ui/popup/disabled.tsx
+++ b/src/ui/popup/disabled.tsx
@@ -16,7 +16,9 @@ export default function Disabled() {
 			<h1>{t('disabledSiteHeader')}</h1>
 			<p>{t('disabledSiteDesc')}</p>
 			<PopupAnchor
-				href={browser.runtime.getURL('src/ui/options/index.html')}
+				href={browser.runtime.getURL(
+					'src/ui/options/index.html?p=connectors',
+				)}
 				class={`${optionComponentStyles.linkButton} ${styles.centered}`}
 			>
 				<Settings />
diff --git a/src/ui/popup/err.tsx b/src/ui/popup/err.tsx
index 21418525..c4695105 100644
--- a/src/ui/popup/err.tsx
+++ b/src/ui/popup/err.tsx
@@ -18,7 +18,9 @@ export default function Err() {
 				<TPopupAnchor messageName="serviceErrorDesc" />
 			</p>
 			<PopupAnchor
-				href={browser.runtime.getURL('src/ui/options/index.html')}
+				href={browser.runtime.getURL(
+					'src/ui/options/index.html?p=accounts',
+				)}
 				class={`${optionComponentStyles.linkButton} ${styles.centered}`}
 			>
 				<Settings />
diff --git a/src/util/notifications.ts b/src/util/notifications.ts
index 48b8fd28..1e521b78 100644
--- a/src/util/notifications.ts
+++ b/src/util/notifications.ts
@@ -6,6 +6,7 @@ import * as Options from '@/core/storage/options';
 import { ConnectorMeta } from '@/core/connectors';
 import { Scrobbler } from '@/core/object/scrobble-service';
 import { debugLog } from '@/core/content/util';
+import * as BrowserStorage from '@/core/storage/browser-storage';
 
 /**
  * Notification service.
@@ -43,6 +44,9 @@ let notificationTimeoutId: NodeJS.Timeout | null = null;
  * @returns Check result
  */
 async function isAvailable() {
+	if (!browser?.notifications) {
+		return false;
+	}
 	// #v-ifdef VITE_CHROME
 	const platform = await getPlatformName();
 	if (platform === 'mac') {
@@ -74,7 +78,7 @@ async function isAllowed(connector: ConnectorMeta) {
  * @param notificationId - Notification ID
  * @param callback - Function that will be called on notification click
  */
-function addOnClickedListener(notificationId: number, callback: () => void) {
+function addOnClickedListener(notificationId: string, callback: () => void) {
 	clickListeners[notificationId] = callback;
 }
 
@@ -112,7 +116,7 @@ interface ProcessedNotificationOptions extends BaseNotificationOptions {
  */
 async function showNotification(
 	options: BaseNotificationOptions,
-	onClick: (() => void) | null
+	onClick: (() => void) | null,
 ) {
 	if (!(await isAvailable())) {
 		throw new Error('Notifications are not available');
@@ -131,7 +135,7 @@ async function showNotification(
 	try {
 		notificationId = await browser.notifications?.create(
 			'',
-			processedOptions
+			processedOptions,
 		);
 	} catch (err) {
 		// Use default track art and try again
@@ -142,12 +146,12 @@ async function showNotification(
 		processedOptions.iconUrl = defaultTrackArtUrl;
 		notificationId = await browser.notifications?.create(
 			'',
-			processedOptions
+			processedOptions,
 		);
 	}
 
 	if (typeof onClick === 'function') {
-		addOnClickedListener(parseInt(notificationId), onClick);
+		addOnClickedListener(notificationId, onClick);
 	}
 
 	return notificationId;
@@ -162,7 +166,7 @@ async function showNotification(
 export async function showNowPlaying(
 	song: BaseSong,
 	connector: ConnectorMeta,
-	onClick: () => void
+	onClick: () => void,
 ): Promise<void> {
 	if (!(await isAllowed(connector))) {
 		return;
@@ -186,7 +190,7 @@ export async function showNowPlaying(
 	if (userPlayCount) {
 		const userPlayCountStr = browser.i18n.getMessage(
 			'infoYourScrobbles',
-			userPlayCount.toString()
+			userPlayCount.toString(),
 		);
 		message = `${message ?? 'null'}\n${userPlayCountStr}`;
 	}
@@ -235,7 +239,7 @@ export function clearNowPlaying(song: BaseSong): void {
  */
 export function showError(
 	message: string,
-	onClick: (() => void) | null = null
+	onClick: (() => void) | null = null,
 ): void {
 	const title = browser.i18n.getMessage('notificationAuthError');
 	const options = { title, message };
@@ -249,11 +253,11 @@ export function showError(
  */
 export function showSignInError(
 	scrobbler: Scrobbler,
-	onClick: () => void
+	onClick: () => void,
 ): void {
 	const errorMessage = browser.i18n.getMessage(
 		'notificationUnableSignIn',
-		scrobbler.getLabel()
+		scrobbler.getLabel(),
 	);
 	showError(errorMessage, onClick);
 }
@@ -267,12 +271,12 @@ export function showSignInError(
 export async function showSongNotRecognized(
 	song: BaseSong,
 	connector: ConnectorMeta,
-	onClick: () => void
+	onClick: () => void,
 ): Promise<void> {
 	if (
 		!(await Options.getOption(
 			Options.USE_UNRECOGNIZED_SONG_NOTIFICATIONS,
-			connector.id
+			connector.id,
 		))
 	) {
 		return;
@@ -284,21 +288,81 @@ export async function showSongNotRecognized(
 		message: browser.i18n.getMessage('notificationNotRecognizedText'),
 	};
 
-	const notificationId = await showNotification(options, onClick);
-	song.metadata.notificationId = notificationId;
+	try {
+		const notificationId = await showNotification(options, onClick);
+		song.metadata.notificationId = notificationId;
+	} catch (err) {
+		debugLog('Unable to show song not recognized notification: ', 'warn');
+		debugLog(err, 'warn');
+	}
+}
+
+/**
+ * How many times to show auth notification.
+ */
+const authNotificationDisplayCount = 3;
+
+/**
+ * Storage for auth notification display count.
+ */
+const notificationStorage = BrowserStorage.getStorage(
+	BrowserStorage.NOTIFICATIONS,
+);
+
+/**
+ * Check if auth notification is allowed.
+ * @returns Check result
+ */
+async function isAuthNotificationAllowed(): Promise<boolean> {
+	const displayCount =
+		(await notificationStorage.get())?.authDisplayCount || 0;
+	return displayCount < authNotificationDisplayCount;
+}
+
+/**
+ * Update internal counter of displayed auth notifications.
+ */
+async function updateAuthDisplayCount() {
+	let data = await notificationStorage.get();
+	if (!data) {
+		data = {
+			authDisplayCount: 0,
+		};
+	}
+
+	data.authDisplayCount = data.authDisplayCount + 1;
+	await notificationStorage.set(data);
 }
 
 /**
  * Show auth notification.
- * @param onClick - Function that will be called on notification click
  */
-export async function showAuthNotification(onClick: () => void): Promise<void> {
+export async function showAuthNotification(): Promise<void> {
+	if (!(await isAuthNotificationAllowed())) {
+		return;
+	}
+
 	const options = {
 		title: browser.i18n.getMessage('notificationConnectAccounts'),
 		message: browser.i18n.getMessage('notificationConnectAccountsText'),
 	};
 
-	await showNotification(options, onClick);
+	try {
+		await showNotification(options, () => {
+			browser.tabs.create({
+				url: browser.runtime.getURL(
+					'src/ui/options/index.html?p=accounts',
+				),
+			});
+		});
+	} catch (err) {
+		debugLog('Unable to show auth notification: ', 'warn');
+		debugLog(err, 'warn');
+		browser.tabs.create({
+			url: browser.runtime.getURL('src/ui/options/index.html?p=accounts'),
+		});
+	}
+	void updateAuthDisplayCount();
 }
 
 /**


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c6e6275b..265d199b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -12,13 +12,10 @@ jobs:
       - name: Checkout the repository
         uses: actions/checkout@v3
 
-      - name: Get Node.js version from .nvmrc
-        run: echo "NODE_VERSION=$(cat .nvmrc)" >> $GITHUB_ENV
-
       - name: Setup Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: '${{ env.NODE_VERSION }}'
+          node-version-file: '.nvmrc'
 
       - name: Install dependencies
         run: npm ci
diff --git a/vitest.config.ts b/vitest.config.ts
index 1d9c25e3..977024a1 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -9,4 +9,7 @@ export default {
 		},
 	},
 	plugins: [ConditionalCompile()],
+	test: {
+		testTimeout: 10000,
+	},
 };