Repository: gitify-app/gitify
Pull request #899 (2024-03-19 08:38:05)

feat: add release user
Add `Release` user notification details.
<img width="500" alt="Screenshot 2024-03-19 at 4 39 43 AM" src="https://github.com/gitify-app/gitify/assets/386277/ad308da3-e3ea-475b-a4d8-488ba7c68f30">


Refactored tests and extracted user lookup into a common function.


Patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 98284a76..2bdc0c8d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -4,6 +4,7 @@ on:
   push:
     branches:
       - 'release/v**'
+  workflow_dispatch: #For manually running release process to verify codesigning of artifacts
 
 jobs:
   release-macos:
@@ -31,6 +32,11 @@ jobs:
           CSC_KEY_PASSWORD: ${{ secrets.mac_certs_password }}
           GH_TOKEN: ${{ secrets.gh_token }}
           NOTARIZE: true
+      - uses: actions/upload-artifact@v4
+        with:
+          name: Gitify-release-mac
+          path: dist/
+          overwrite: true
 
   release-windows:
     name: Publish Windows (electron-builder)
@@ -51,6 +57,11 @@ jobs:
       - run: pnpm make:win --publish onTagOrDraft
         env:
           GH_TOKEN: ${{ secrets.gh_token }}
+      - uses: actions/upload-artifact@v4
+        with:
+          name: Gitify-release-win
+          path: dist/
+          overwrite: true
 
   release-linux:
     name: Publish Linux (electron-builder)
@@ -71,3 +82,8 @@ jobs:
       - run: pnpm make:linux --publish onTagOrDraft
         env:
           GH_TOKEN: ${{ secrets.gh_token }}
+      - uses: actions/upload-artifact@v4
+        with:
+          name: Gitify-release-linux
+          path: dist/
+          overwrite: true
diff --git a/package.json b/package.json
index a84432a2..0d0dedf7 100644
--- a/package.json
+++ b/package.json
@@ -103,7 +103,7 @@
   },
   "dependencies": {
     "@electron/remote": "2.1.2",
-    "@primer/octicons-react": "19.8.0",
+    "@primer/octicons-react": "19.9.0",
     "axios": "1.6.8",
     "date-fns": "3.6.0",
     "electron-updater": "6.1.8",
@@ -120,23 +120,23 @@
   },
   "devDependencies": {
     "@electron/notarize": "2.3.0",
-    "@testing-library/react": "14.2.1",
+    "@testing-library/react": "14.2.2",
     "@types/jest": "29.5.12",
-    "@types/node": "20.11.28",
+    "@types/node": "20.11.30",
     "@types/nprogress": "0.2.3",
-    "@types/react": "18.2.66",
+    "@types/react": "18.2.67",
     "@types/react-router-dom": "5.3.3",
     "@types/react-test-renderer": "18.0.7",
     "@types/react-transition-group": "4.4.10",
     "autoprefixer": "10.4.18",
     "css-loader": "6.10.0",
-    "electron": "29.1.4",
+    "electron": "19.1.9",
     "electron-builder": "24.13.3",
     "husky": "9.0.11",
     "jest": "29.7.0",
     "jest-environment-jsdom": "29.7.0",
     "nock": "13.5.4",
-    "postcss": "8.4.36",
+    "postcss": "8.4.37",
     "postcss-loader": "8.1.1",
     "prettier": "3.2.5",
     "react-test-renderer": "18.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 23f57bc3..26737f8e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -10,10 +10,10 @@ overrides:
 dependencies:
   '@electron/remote':
     specifier: 2.1.2
-    version: 2.1.2(electron@29.1.4)
+    version: 2.1.2(electron@19.1.9)
   '@primer/octicons-react':
-    specifier: 19.8.0
-    version: 19.8.0(react@18.2.0)
+    specifier: 19.9.0
+    version: 19.9.0(react@18.2.0)
   axios:
     specifier: 1.6.8
     version: 1.6.8
@@ -28,7 +28,7 @@ dependencies:
     version: 4.20.10
   menubar:
     specifier: 9.4.0
-    version: 9.4.0(electron@29.1.4)
+    version: 9.4.0(electron@19.1.9)
   nprogress:
     specifier: 0.2.0
     version: 0.2.0
@@ -59,20 +59,20 @@ devDependencies:
     specifier: 2.3.0
     version: 2.3.0
   '@testing-library/react':
-    specifier: 14.2.1
-    version: 14.2.1(react-dom@18.2.0)(react@18.2.0)
+    specifier: 14.2.2
+    version: 14.2.2(react-dom@18.2.0)(react@18.2.0)
   '@types/jest':
     specifier: 29.5.12
     version: 29.5.12
   '@types/node':
-    specifier: 20.11.28
-    version: 20.11.28
+    specifier: 20.11.30
+    version: 20.11.30
   '@types/nprogress':
     specifier: 0.2.3
     version: 0.2.3
   '@types/react':
-    specifier: 18.2.66
-    version: 18.2.66
+    specifier: 18.2.67
+    version: 18.2.67
   '@types/react-router-dom':
     specifier: 5.3.3
     version: 5.3.3
@@ -84,13 +84,13 @@ devDependencies:
     version: 4.4.10
   autoprefixer:
     specifier: 10.4.18
-    version: 10.4.18(postcss@8.4.36)
+    version: 10.4.18(postcss@8.4.37)
   css-loader:
     specifier: 6.10.0
     version: 6.10.0(webpack@5.90.3)
   electron:
-    specifier: 29.1.4
-    version: 29.1.4
+    specifier: 19.1.9
+    version: 19.1.9
   electron-builder:
     specifier: 24.13.3
     version: 24.13.3(electron-builder-squirrel-windows@24.13.3)
@@ -99,7 +99,7 @@ devDependencies:
     version: 9.0.11
   jest:
     specifier: 29.7.0
-    version: 29.7.0(@types/node@20.11.28)
+    version: 29.7.0(@types/node@20.11.30)
   jest-environment-jsdom:
     specifier: 29.7.0
     version: 29.7.0
@@ -107,11 +107,11 @@ devDependencies:
     specifier: 13.5.4
     version: 13.5.4
   postcss:
-    specifier: 8.4.36
-    version: 8.4.36
+    specifier: 8.4.37
+    version: 8.4.37
   postcss-loader:
     specifier: 8.1.1
-    version: 8.1.1(postcss@8.4.36)(typescript@5.4.2)(webpack@5.90.3)
+    version: 8.1.1(postcss@8.4.37)(typescript@5.4.2)(webpack@5.90.3)
   prettier:
     specifier: 3.2.5
     version: 3.2.5
@@ -184,7 +184,7 @@ packages:
     engines: {node: '>=6.9.0'}
     dependencies:
       '@ampproject/remapping': 2.2.1
-      '@babel/code-frame': 7.21.4
+      '@babel/code-frame': 7.23.5
       '@babel/generator': 7.21.5
       '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8)
       '@babel/helper-module-transforms': 7.21.5
@@ -276,7 +276,7 @@ packages:
       '@babel/helper-module-imports': 7.21.4
       '@babel/helper-simple-access': 7.21.5
       '@babel/helper-split-export-declaration': 7.18.6
-      '@babel/helper-validator-identifier': 7.19.1
+      '@babel/helper-validator-identifier': 7.22.20
       '@babel/template': 7.20.7
       '@babel/traverse': 7.24.0
       '@babel/types': 7.21.5
@@ -519,7 +519,7 @@ packages:
     resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/code-frame': 7.21.4
+      '@babel/code-frame': 7.23.5
       '@babel/parser': 7.21.8
       '@babel/types': 7.21.5
     dev: true
@@ -556,7 +556,7 @@ packages:
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/helper-string-parser': 7.21.5
-      '@babel/helper-validator-identifier': 7.19.1
+      '@babel/helper-validator-identifier': 7.22.20
       to-fast-properties: 2.0.0
     dev: true
 
@@ -595,19 +595,20 @@ packages:
       minimatch: 3.1.2
     dev: true
 
-  /@electron/get@2.0.3:
-    resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==}
-    engines: {node: '>=12'}
+  /@electron/get@1.14.1:
+    resolution: {integrity: sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==}
+    engines: {node: '>=8.6'}
     dependencies:
       debug: 4.3.4
       env-paths: 2.2.1
       fs-extra: 8.1.0
-      got: 11.8.6
+      got: 9.6.0
       progress: 2.0.3
       semver: 6.3.1
       sumchecker: 3.0.1
     optionalDependencies:
       global-agent: 3.0.0
+      global-tunnel-ng: 2.7.1
     transitivePeerDependencies:
       - supports-color
 
@@ -648,12 +649,12 @@ packages:
       - supports-color
     dev: true
 
-  /@electron/remote@2.1.2(electron@29.1.4):
+  /@electron/remote@2.1.2(electron@19.1.9):
     resolution: {integrity: sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==}
     peerDependencies:
       electron: '>= 13.0.0'
     dependencies:
-      electron: 29.1.4
+      electron: 19.1.9
     dev: false
 
   /@electron/universal@1.5.1:
@@ -704,7 +705,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -725,14 +726,14 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       ci-info: 3.8.0
       exit: 0.1.2
       graceful-fs: 4.2.11
       jest-changed-files: 29.7.0
-      jest-config: 29.7.0(@types/node@20.11.28)
+      jest-config: 29.7.0(@types/node@20.11.30)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -760,7 +761,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       jest-mock: 29.7.0
     dev: true
 
@@ -787,7 +788,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -820,7 +821,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -908,7 +909,7 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       '@types/yargs': 17.0.25
       chalk: 4.1.2
     dev: true
@@ -1002,8 +1003,8 @@ packages:
     dev: true
     optional: true
 
-  /@primer/octicons-react@19.8.0(react@18.2.0):
-    resolution: {integrity: sha512-2Z+D7xTloFTLQVRUEbg0pQpe6aTL9RR+8RqBhjkrF+BFuVdM1ENOyjywaGEO7DIkPU5Zxlv0gxSlD85LQxL+sw==}
+  /@primer/octicons-react@19.9.0(react@18.2.0):
+    resolution: {integrity: sha512-Uk4XrHyfylyfzZN9d8VFjF8FpfYHEyT4sabw+9+oP+GWAJHhPvNPTz6gXvUzJZmoblAvgcTrDslIPjz8zMh76w==}
     engines: {node: '>=8'}
     peerDependencies:
       react: '>=16.3'
@@ -1020,9 +1021,9 @@ packages:
     resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
     dev: true
 
-  /@sindresorhus/is@4.6.0:
-    resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
-    engines: {node: '>=10'}
+  /@sindresorhus/is@0.14.0:
+    resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==}
+    engines: {node: '>=6'}
 
   /@sinonjs/commons@3.0.0:
     resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==}
@@ -1036,17 +1037,17 @@ packages:
       '@sinonjs/commons': 3.0.0
     dev: true
 
-  /@szmarczak/http-timer@4.0.6:
-    resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
-    engines: {node: '>=10'}
+  /@szmarczak/http-timer@1.1.2:
+    resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==}
+    engines: {node: '>=6'}
     dependencies:
-      defer-to-connect: 2.0.1
+      defer-to-connect: 1.1.3
 
   /@testing-library/dom@9.3.3:
     resolution: {integrity: sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==}
     engines: {node: '>=14'}
     dependencies:
-      '@babel/code-frame': 7.21.4
+      '@babel/code-frame': 7.23.5
       '@babel/runtime': 7.21.5
       '@types/aria-query': 5.0.2
       aria-query: 5.1.3
@@ -1056,8 +1057,8 @@ packages:
       pretty-format: 27.5.1
     dev: true
 
-  /@testing-library/react@14.2.1(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-sGdjws32ai5TLerhvzThYFbpnF9XtL65Cjf+gB0Dhr29BGqK+mAeN7SURSdu+eqgET4ANcWoC7FQpkaiGvBr+A==}
+  /@testing-library/react@14.2.2(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-SOUuM2ysCvjUWBXTNfQ/ztmnKDmqaiPV3SvoIuyxMUca45rbSWWAT/qB8CUs/JQ/ux/8JFs9DNdFQ3f6jH3crA==}
     engines: {node: '>=14'}
     peerDependencies:
       react: ^18.0.0
@@ -1108,14 +1109,6 @@ packages:
       '@babel/types': 7.21.5
     dev: true
 
-  /@types/cacheable-request@6.0.3:
-    resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
-    dependencies:
-      '@types/http-cache-semantics': 4.0.4
-      '@types/keyv': 3.1.4
-      '@types/node': 20.11.28
-      '@types/responselike': 1.0.3
-
   /@types/debug@4.1.7:
     resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==}
     dependencies:
@@ -1140,22 +1133,19 @@ packages:
   /@types/fs-extra@9.0.13:
     resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
     dependencies:
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
     dev: true
 
   /@types/graceful-fs@4.1.6:
     resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
     dependencies:
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
     dev: true
 
   /@types/history@4.7.11:
     resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==}
     dev: true
 
-  /@types/http-cache-semantics@4.0.4:
-    resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
-
   /@types/istanbul-lib-coverage@2.0.4:
     resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
     dev: true
@@ -1182,7 +1172,7 @@ packages:
   /@types/jsdom@20.0.1:
     resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==}
     dependencies:
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       '@types/tough-cookie': 4.0.5
       parse5: 7.1.2
     dev: true
@@ -1193,14 +1183,17 @@ packages:
   /@types/keyv@3.1.4:
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
     dependencies:
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
 
   /@types/ms@0.7.31:
     resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
     dev: true
 
-  /@types/node@20.11.28:
-    resolution: {integrity: sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==}
+  /@types/node@16.18.91:
+    resolution: {integrity: sha512-h8Q4klc8xzc9kJKr7UYNtJde5TU2qEePVyH3WyzJaUC+3ptyc5kPQbWOIUcn8ZsG5+KSkq+P0py0kC0VqxgAXw==}
+
+  /@types/node@20.11.30:
+    resolution: {integrity: sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==}
     dependencies:
       undici-types: 5.26.5
 
@@ -1212,7 +1205,7 @@ packages:
     resolution: {integrity: sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==}
     requiresBuild: true
     dependencies:
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       xmlbuilder: 15.1.1
     dev: true
     optional: true
@@ -1224,14 +1217,14 @@ packages:
   /@types/react-dom@18.2.8:
     resolution: {integrity: sha512-bAIvO5lN/U8sPGvs1Xm61rlRHHaq5rp5N3kp9C+NJ/Q41P8iqjkXSu0+/qu8POsjH9pNWb0OYabFez7taP7omw==}
     dependencies:
-      '@types/react': 18.2.66
+      '@types/react': 18.2.67
     dev: true
 
   /@types/react-router-dom@5.3.3:
     resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
     dependencies:
       '@types/history': 4.7.11
-      '@types/react': 18.2.66
+      '@types/react': 18.2.67
       '@types/react-router': 5.1.20
     dev: true
 
@@ -1239,23 +1232,23 @@ packages:
     resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
     dependencies:
       '@types/history': 4.7.11
-      '@types/react': 18.2.66
+      '@types/react': 18.2.67
     dev: true
 
   /@types/react-test-renderer@18.0.7:
     resolution: {integrity: sha512-1+ANPOWc6rB3IkSnElhjv6VLlKg2dSv/OWClUyZimbLsQyBn8Js9Vtdsi3UICJ2rIQ3k2la06dkB+C92QfhKmg==}
     dependencies:
-      '@types/react': 18.2.66
+      '@types/react': 18.2.67
     dev: true
 
   /@types/react-transition-group@4.4.10:
     resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==}
     dependencies:
-      '@types/react': 18.2.66
+      '@types/react': 18.2.67
     dev: true
 
-  /@types/react@18.2.66:
-    resolution: {integrity: sha512-OYTmMI4UigXeFMF/j4uv0lBBEbongSgptPrHBxqME44h9+yNov+oL6Z3ocJKo0WyXR84sQUNeyIp9MRfckvZpg==}
+  /@types/react@18.2.67:
+    resolution: {integrity: sha512-vkIE2vTIMHQ/xL0rgmuoECBCkZFZeHr49HeWSc24AptMbNRo7pwSBvj73rlJJs9fGKj0koS+V7kQB1jHS0uCgw==}
     dependencies:
       '@types/prop-types': 15.7.5
       '@types/scheduler': 0.16.4
@@ -1265,7 +1258,7 @@ packages:
   /@types/responselike@1.0.3:
     resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
     dependencies:
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
 
   /@types/scheduler@0.16.4:
     resolution: {integrity: sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==}
@@ -1295,13 +1288,6 @@ packages:
       '@types/yargs-parser': 21.0.0
     dev: true
 
-  /@types/yauzl@2.10.3:
-    resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
-    requiresBuild: true
-    dependencies:
-      '@types/node': 20.11.28
-    optional: true
-
   /@webassemblyjs/ast@1.11.6:
     resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==}
     dependencies:
@@ -1684,7 +1670,7 @@ packages:
     engines: {node: '>= 4.0.0'}
     dev: true
 
-  /autoprefixer@10.4.18(postcss@8.4.36):
+  /autoprefixer@10.4.18(postcss@8.4.37):
     resolution: {integrity: sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==}
     engines: {node: ^10 || ^12 || >=14}
     hasBin: true
@@ -1696,7 +1682,7 @@ packages:
       fraction.js: 4.3.7
       normalize-range: 0.1.2
       picocolors: 1.0.0
-      postcss: 8.4.36
+      postcss: 8.4.37
       postcss-value-parser: 4.2.0
     dev: true
 
@@ -1939,21 +1925,17 @@ packages:
       - supports-color
     dev: true
 
-  /cacheable-lookup@5.0.4:
-    resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
-    engines: {node: '>=10.6.0'}
-
-  /cacheable-request@7.0.4:
-    resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
+  /cacheable-request@6.1.0:
+    resolution: {integrity: sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==}
     engines: {node: '>=8'}
     dependencies:
       clone-response: 1.0.3
       get-stream: 5.2.0
       http-cache-semantics: 4.1.1
-      keyv: 4.5.4
+      keyv: 3.1.0
       lowercase-keys: 2.0.0
-      normalize-url: 6.1.0
-      responselike: 2.0.1
+      normalize-url: 4.5.1
+      responselike: 1.0.2
 
   /call-bind@1.0.2:
     resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
@@ -2154,6 +2136,23 @@ packages:
     resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
     dev: true
 
+  /concat-stream@1.6.2:
+    resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
+    engines: {'0': node >= 0.8}
+    dependencies:
+      buffer-from: 1.1.2
+      inherits: 2.0.4
+      readable-stream: 2.3.8
+      typedarray: 0.0.6
+
+  /config-chain@1.1.13:
+    resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+    requiresBuild: true
+    dependencies:
+      ini: 1.3.8
+      proto-list: 1.2.4
+    optional: true
+
   /config-file-ts@0.2.4:
     resolution: {integrity: sha512-cKSW0BfrSaAUnxpgvpXPLaaW/umg4bqg4k3GO1JqlRfpx+d5W0GDXznCMkWotJQek5Mmz1MJVChQnz3IVaeMZQ==}
     dependencies:
@@ -2172,7 +2171,6 @@ packages:
   /core-util-is@1.0.2:
     resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
     requiresBuild: true
-    dev: true
 
   /cosmiconfig@9.0.0(typescript@5.4.2):
     resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
@@ -2212,7 +2210,7 @@ packages:
     dev: true
     optional: true
 
-  /create-jest@29.7.0(@types/node@20.11.28):
+  /create-jest@29.7.0(@types/node@20.11.30):
     resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -2221,7 +2219,7 @@ packages:
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.11.28)
+      jest-config: 29.7.0(@types/node@20.11.30)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -2251,12 +2249,12 @@ packages:
       webpack:
         optional: true
     dependencies:
-      icss-utils: 5.1.0(postcss@8.4.36)
-      postcss: 8.4.36
-      postcss-modules-extract-imports: 3.0.0(postcss@8.4.36)
-      postcss-modules-local-by-default: 4.0.4(postcss@8.4.36)
-      postcss-modules-scope: 3.1.1(postcss@8.4.36)
-      postcss-modules-values: 4.0.0(postcss@8.4.36)
+      icss-utils: 5.1.0(postcss@8.4.37)
+      postcss: 8.4.37
+      postcss-modules-extract-imports: 3.0.0(postcss@8.4.37)
+      postcss-modules-local-by-default: 4.0.4(postcss@8.4.37)
+      postcss-modules-scope: 3.1.1(postcss@8.4.37)
+      postcss-modules-values: 4.0.0(postcss@8.4.37)
       postcss-value-parser: 4.2.0
       semver: 7.5.4
       webpack: 5.90.3(webpack-cli@5.1.4)
@@ -2299,6 +2297,16 @@ packages:
     resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
     dev: false
 
+  /debug@2.6.9:
+    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.0.0
+
   /debug@4.3.4:
     resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
     engines: {node: '>=6.0'}
@@ -2314,11 +2322,11 @@ packages:
     resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
     dev: true
 
-  /decompress-response@6.0.0:
-    resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
-    engines: {node: '>=10'}
+  /decompress-response@3.3.0:
+    resolution: {integrity: sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==}
+    engines: {node: '>=4'}
     dependencies:
-      mimic-response: 3.1.0
+      mimic-response: 1.0.1
 
   /dedent@1.5.1:
     resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==}
@@ -2357,9 +2365,8 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
-  /defer-to-connect@2.0.1:
-    resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
-    engines: {node: '>=10'}
+  /defer-to-connect@1.1.3:
+    resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==}
 
   /define-data-property@1.1.1:
     resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
@@ -2475,6 +2482,9 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /duplexer3@0.1.5:
+    resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
+
   /eastasianwidth@0.2.0:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
     dev: true
@@ -2560,15 +2570,15 @@ packages:
       - supports-color
     dev: false
 
-  /electron@29.1.4:
-    resolution: {integrity: sha512-IWXys0SqgmIfrqXusUGQC0gGG7CCqA5vfmNsUMj8dFkAnK3lisKyjSESStWlrsste/OX/AAC5wsVlf23reUNnw==}
-    engines: {node: '>= 12.20.55'}
+  /electron@19.1.9:
+    resolution: {integrity: sha512-XT5LkTzIHB+ZtD3dTmNnKjVBWrDWReCKt9G1uAFLz6uJMEVcIUiYO+fph5pLXETiBw/QZBx8egduMEfIccLx+g==}
+    engines: {node: '>= 8.6'}
     hasBin: true
     requiresBuild: true
     dependencies:
-      '@electron/get': 2.0.3
-      '@types/node': 20.11.28
-      extract-zip: 2.0.1
+      '@electron/get': 1.14.1
+      '@types/node': 16.18.91
+      extract-zip: 1.7.0
     transitivePeerDependencies:
       - supports-color
 
@@ -2586,6 +2596,12 @@ packages:
     resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
     dev: true
 
+  /encodeurl@1.0.2:
+    resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+    engines: {node: '>= 0.8'}
+    requiresBuild: true
+    optional: true
+
   /end-of-stream@1.4.4:
     resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
     dependencies:
@@ -2743,16 +2759,14 @@ packages:
       jest-util: 29.7.0
     dev: true
 
-  /extract-zip@2.0.1:
-    resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
-    engines: {node: '>= 10.17.0'}
+  /extract-zip@1.7.0:
+    resolution: {integrity: sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==}
     hasBin: true
     dependencies:
-      debug: 4.3.4
-      get-stream: 5.2.0
+      concat-stream: 1.6.2
+      debug: 2.6.9
+      mkdirp: 0.5.6
       yauzl: 2.10.0
-    optionalDependencies:
-      '@types/yauzl': 2.10.3
     transitivePeerDependencies:
       - supports-color
 
@@ -2949,6 +2963,12 @@ packages:
     engines: {node: '>=8.0.0'}
     dev: true
 
+  /get-stream@4.1.0:
+    resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==}
+    engines: {node: '>=6'}
+    dependencies:
+      pump: 3.0.0
+
   /get-stream@5.2.0:
     resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
     engines: {node: '>=8'}
@@ -3024,6 +3044,17 @@ packages:
       serialize-error: 7.0.1
     optional: true
 
+  /global-tunnel-ng@2.7.1:
+    resolution: {integrity: sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==}
+    engines: {node: '>=0.10'}
+    requiresBuild: true
+    dependencies:
+      encodeurl: 1.0.2
+      lodash: 4.17.21
+      npm-conf: 1.1.3
+      tunnel: 0.0.6
+    optional: true
+
   /globals@11.12.0:
     resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
     engines: {node: '>=4'}
@@ -3042,21 +3073,23 @@ packages:
     dependencies:
       get-intrinsic: 1.2.2
 
-  /got@11.8.6:
-    resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==}
-    engines: {node: '>=10.19.0'}
+  /got@9.6.0:
+    resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==}
+    engines: {node: '>=8.6'}
     dependencies:
-      '@sindresorhus/is': 4.6.0
-      '@szmarczak/http-timer': 4.0.6
-      '@types/cacheable-request': 6.0.3
+      '@sindresorhus/is': 0.14.0
+      '@szmarczak/http-timer': 1.1.2
+      '@types/keyv': 3.1.4
       '@types/responselike': 1.0.3
-      cacheable-lookup: 5.0.4
-      cacheable-request: 7.0.4
-      decompress-response: 6.0.0
-      http2-wrapper: 1.0.3
-      lowercase-keys: 2.0.0
-      p-cancelable: 2.1.1
-      responselike: 2.0.1
+      cacheable-request: 6.1.0
+      decompress-response: 3.3.0
+      duplexer3: 0.1.5
+      get-stream: 4.1.0
+      lowercase-keys: 1.0.1
+      mimic-response: 1.0.1
+      p-cancelable: 1.1.0
+      to-readable-stream: 1.0.0
+      url-parse-lax: 3.0.0
 
   /graceful-fs@4.2.11:
     resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@@ -3142,13 +3175,6 @@ packages:
       - supports-color
     dev: true
 
-  /http2-wrapper@1.0.3:
-    resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==}
-    engines: {node: '>=10.19.0'}
-    dependencies:
-      quick-lru: 5.1.1
-      resolve-alpn: 1.2.1
-
   /https-proxy-agent@5.0.1:
     resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
     engines: {node: '>= 6'}
@@ -3188,13 +3214,13 @@ packages:
       safer-buffer: 2.1.2
     dev: true
 
-  /icss-utils@5.1.0(postcss@8.4.36):
+  /icss-utils@5.1.0(postcss@8.4.37):
     resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
     engines: {node: ^10 || ^12 || >= 14}
     peerDependencies:
       postcss: ^8.1.0
     dependencies:
-      postcss: 8.4.36
+      postcss: 8.4.37
     dev: true
 
   /ieee754@1.2.1:
@@ -3232,7 +3258,11 @@ packages:
 
   /inherits@2.0.4:
     resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
-    dev: true
+
+  /ini@1.3.8:
+    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+    requiresBuild: true
+    optional: true
 
   /internal-slot@1.0.5:
     resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
@@ -3417,7 +3447,6 @@ packages:
 
   /isarray@1.0.0:
     resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
-    dev: true
 
   /isarray@2.0.5:
     resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
@@ -3536,7 +3565,7 @@ packages:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.5.1
@@ -3557,7 +3586,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-cli@29.7.0(@types/node@20.11.28):
+  /jest-cli@29.7.0(@types/node@20.11.30):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -3571,10 +3600,10 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
       chalk: 4.1.2
-      create-jest: 29.7.0(@types/node@20.11.28)
+      create-jest: 29.7.0(@types/node@20.11.30)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.11.28)
+      jest-config: 29.7.0(@types/node@20.11.30)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.7.2
@@ -3585,7 +3614,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest-config@29.7.0(@types/node@20.11.28):
+  /jest-config@29.7.0(@types/node@20.11.30):
     resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     peerDependencies:
@@ -3600,7 +3629,7 @@ packages:
       '@babel/core': 7.21.8
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       babel-jest: 29.7.0(@babel/core@7.21.8)
       chalk: 4.1.2
       ci-info: 3.8.0
@@ -3666,7 +3695,7 @@ packages:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
       '@types/jsdom': 20.0.1
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       jest-mock: 29.7.0
       jest-util: 29.7.0
       jsdom: 20.0.3
@@ -3683,7 +3712,7 @@ packages:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       jest-mock: 29.7.0
       jest-util: 29.7.0
     dev: true
@@ -3699,7 +3728,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -3750,7 +3779,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       jest-util: 29.7.0
     dev: true
 
@@ -3805,7 +3834,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -3836,7 +3865,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       chalk: 4.1.2
       cjs-module-lexer: 1.2.3
       collect-v8-coverage: 1.0.1
@@ -3888,7 +3917,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       chalk: 4.1.2
       ci-info: 3.8.0
       graceful-fs: 4.2.11
@@ -3913,7 +3942,7 @@ packages:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -3925,7 +3954,7 @@ packages:
     resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
     engines: {node: '>= 10.13.0'}
     dependencies:
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
@@ -3933,13 +3962,13 @@ packages:
     resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@types/node': 20.11.28
+      '@types/node': 20.11.30
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: true
 
-  /jest@29.7.0(@types/node@20.11.28):
+  /jest@29.7.0(@types/node@20.11.30):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -3952,7 +3981,7 @@ packages:
       '@jest/core': 29.7.0
       '@jest/types': 29.6.3
       import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.11.28)
+      jest-cli: 29.7.0(@types/node@20.11.30)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -4029,8 +4058,8 @@ packages:
     hasBin: true
     dev: true
 
-  /json-buffer@3.0.1:
-    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+  /json-buffer@3.0.0:
+    resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==}
 
   /json-parse-even-better-errors@2.3.1:
     resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
@@ -4060,10 +4089,10 @@ packages:
     optionalDependencies:
       graceful-fs: 4.2.11
 
-  /keyv@4.5.4:
-    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+  /keyv@3.1.0:
+    resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==}
     dependencies:
-      json-buffer: 3.0.1
+      json-buffer: 3.0.0
 
   /kind-of@6.0.3:
     resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
@@ -4142,7 +4171,6 @@ packages:
 
   /lodash@4.17.21:
     resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
-    dev: true
 
   /loose-envify@1.4.0:
     resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
@@ -4150,6 +4178,10 @@ packages:
     dependencies:
       js-tokens: 4.0.0
 
+  /lowercase-keys@1.0.1:
+    resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==}
+    engines: {node: '>=0.10.0'}
+
   /lowercase-keys@2.0.0:
     resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
     engines: {node: '>=8'}
@@ -4201,12 +4233,12 @@ packages:
       escape-string-regexp: 4.0.0
     optional: true
 
-  /menubar@9.4.0(electron@29.1.4):
+  /menubar@9.4.0(electron@19.1.9):
     resolution: {integrity: sha512-Zn2si5UXiWbdBZwFSsCyQ8xfaBKkeFYceNvjWATyMUreqv6gDMUvDkvZY/ZFf89DCfo9F0SrvygDOdpZuwoWgw==}
     peerDependencies:
       electron: ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0 ||  ^23.0.0 || ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0
     dependencies:
-      electron: 29.1.4
+      electron: 19.1.9
       electron-positioner: 4.1.0
     dev: false
 
@@ -4250,10 +4282,6 @@ packages:
     resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
     engines: {node: '>=4'}
 
-  /mimic-response@3.1.0:
-    resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
-    engines: {node: '>=10'}
-
   /minimatch@3.1.2:
     resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
     dependencies:
@@ -4276,7 +4304,6 @@ packages:
 
   /minimist@1.2.8:
     resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
-    dev: true
 
   /minipass@3.3.6:
     resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
@@ -4298,12 +4325,21 @@ packages:
       yallist: 4.0.0
     dev: true
 
+  /mkdirp@0.5.6:
+    resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+    hasBin: true
+    dependencies:
+      minimist: 1.2.8
+
   /mkdirp@1.0.4:
     resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
     engines: {node: '>=10'}
     hasBin: true
     dev: true
 
+  /ms@2.0.0:
+    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+
   /ms@2.1.2:
     resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
 
@@ -4362,9 +4398,18 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
-  /normalize-url@6.1.0:
-    resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
-    engines: {node: '>=10'}
+  /normalize-url@4.5.1:
+    resolution: {integrity: sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==}
+    engines: {node: '>=8'}
+
+  /npm-conf@1.1.3:
+    resolution: {integrity: sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==}
+    engines: {node: '>=4'}
+    requiresBuild: true
+    dependencies:
+      config-chain: 1.1.13
+      pify: 3.0.0
+    optional: true
 
   /npm-run-path@4.0.1:
     resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
@@ -4429,9 +4474,9 @@ packages:
       mimic-fn: 2.1.0
     dev: true
 
-  /p-cancelable@2.1.1:
-    resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
-    engines: {node: '>=8'}
+  /p-cancelable@1.1.0:
+    resolution: {integrity: sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==}
+    engines: {node: '>=6'}
 
   /p-limit@2.3.0:
     resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
@@ -4518,6 +4563,12 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /pify@3.0.0:
+    resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==}
+    engines: {node: '>=4'}
+    requiresBuild: true
+    optional: true
+
   /pirates@4.0.5:
     resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==}
     engines: {node: '>= 6'}
@@ -4537,29 +4588,29 @@ packages:
       xmlbuilder: 15.1.1
     dev: true
 
-  /postcss-import@15.1.0(postcss@8.4.36):
+  /postcss-import@15.1.0(postcss@8.4.37):
     resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
       postcss: ^8.0.0
     dependencies:
-      postcss: 8.4.36
+      postcss: 8.4.37
       postcss-value-parser: 4.2.0
       read-cache: 1.0.0
       resolve: 1.22.2
     dev: true
 
-  /postcss-js@4.0.1(postcss@8.4.36):
+  /postcss-js@4.0.1(postcss@8.4.37):
     resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
     engines: {node: ^12 || ^14 || >= 16}
     peerDependencies:
       postcss: ^8.4.21
     dependencies:
       camelcase-css: 2.0.1
-      postcss: 8.4.36
+      postcss: 8.4.37
     dev: true
 
-  /postcss-load-config@4.0.1(postcss@8.4.36):
+  /postcss-load-config@4.0.1(postcss@8.4.37):
     resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==}
     engines: {node: '>= 14'}
     peerDependencies:
@@ -4572,11 +4623,11 @@ packages:
         optional: true
     dependencies:
       lilconfig: 2.1.0
-      postcss: 8.4.36
+      postcss: 8.4.37
       yaml: 2.3.2
     dev: true
 
-  /postcss-loader@8.1.1(postcss@8.4.36)(typescript@5.4.2)(webpack@5.90.3):
+  /postcss-loader@8.1.1(postcss@8.4.37)(typescript@5.4.2)(webpack@5.90.3):
     resolution: {integrity: sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==}
     engines: {node: '>= 18.12.0'}
     peerDependencies:
@@ -4591,61 +4642,61 @@ packages:
     dependencies:
       cosmiconfig: 9.0.0(typescript@5.4.2)
       jiti: 1.20.0
-      postcss: 8.4.36
+      postcss: 8.4.37
       semver: 7.5.4
       webpack: 5.90.3(webpack-cli@5.1.4)
     transitivePeerDependencies:
       - typescript
     dev: true
 
-  /postcss-modules-extract-imports@3.0.0(postcss@8.4.36):
+  /postcss-modules-extract-imports@3.0.0(postcss@8.4.37):
     resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==}
     engines: {node: ^10 || ^12 || >= 14}
     peerDependencies:
       postcss: ^8.1.0
     dependencies:
-      postcss: 8.4.36
+      postcss: 8.4.37
     dev: true
 
-  /postcss-modules-local-by-default@4.0.4(postcss@8.4.36):
+  /postcss-modules-local-by-default@4.0.4(postcss@8.4.37):
     resolution: {integrity: sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==}
     engines: {node: ^10 || ^12 || >= 14}
     peerDependencies:
       postcss: ^8.1.0
     dependencies:
-      icss-utils: 5.1.0(postcss@8.4.36)
-      postcss: 8.4.36
+      icss-utils: 5.1.0(postcss@8.4.37)
+      postcss: 8.4.37
       postcss-selector-parser: 6.0.13
       postcss-value-parser: 4.2.0
     dev: true
 
-  /postcss-modules-scope@3.1.1(postcss@8.4.36):
+  /postcss-modules-scope@3.1.1(postcss@8.4.37):
     resolution: {integrity: sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==}
     engines: {node: ^10 || ^12 || >= 14}
     peerDependencies:
       postcss: ^8.1.0
     dependencies:
-      postcss: 8.4.36
+      postcss: 8.4.37
       postcss-selector-parser: 6.0.13
     dev: true
 
-  /postcss-modules-values@4.0.0(postcss@8.4.36):
+  /postcss-modules-values@4.0.0(postcss@8.4.37):
     resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==}
     engines: {node: ^10 || ^12 || >= 14}
     peerDependencies:
       postcss: ^8.1.0
     dependencies:
-      icss-utils: 5.1.0(postcss@8.4.36)
-      postcss: 8.4.36
+      icss-utils: 5.1.0(postcss@8.4.37)
+      postcss: 8.4.37
     dev: true
 
-  /postcss-nested@6.0.1(postcss@8.4.36):
+  /postcss-nested@6.0.1(postcss@8.4.37):
     resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
     engines: {node: '>=12.0'}
     peerDependencies:
       postcss: ^8.2.14
     dependencies:
-      postcss: 8.4.36
+      postcss: 8.4.37
       postcss-selector-parser: 6.0.13
     dev: true
 
@@ -4661,15 +4712,19 @@ packages:
     resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
     dev: true
 
-  /postcss@8.4.36:
-    resolution: {integrity: sha512-/n7eumA6ZjFHAsbX30yhHup/IMkOmlmvtEi7P+6RMYf+bGJSUHc3geH4a0NSZxAz/RJfiS9tooCTs9LAVYUZKw==}
+  /postcss@8.4.37:
+    resolution: {integrity: sha512-7iB/v/r7Woof0glKLH8b1SPHrsX7uhdO+Geb41QpF/+mWZHU3uxxSlN+UXGVit1PawOYDToO+AbZzhBzWRDwbQ==}
     engines: {node: ^10 || ^12 || >=14}
     dependencies:
       nanoid: 3.3.7
       picocolors: 1.0.0
-      source-map-js: 1.1.0
+      source-map-js: 1.2.0
     dev: true
 
+  /prepend-http@2.0.0:
+    resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==}
+    engines: {node: '>=4'}
+
   /prettier@3.2.5:
     resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
     engines: {node: '>=14'}
@@ -4696,7 +4751,6 @@ packages:
 
   /process-nextick-args@2.0.1:
     resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
-    dev: true
 
   /progress@2.0.3:
     resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
@@ -4731,6 +4785,11 @@ packages:
     engines: {node: '>= 8'}
     dev: true
 
+  /proto-list@1.2.4:
+    resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+    requiresBuild: true
+    optional: true
+
   /proxy-from-env@1.1.0:
     resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
     dev: false
@@ -4761,10 +4820,6 @@ packages:
     resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
     dev: true
 
-  /quick-lru@5.1.1:
-    resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
-    engines: {node: '>=10'}
-
   /randombytes@2.1.0:
     resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
     dependencies:
@@ -4894,7 +4949,6 @@ packages:
       safe-buffer: 5.1.2
       string_decoder: 1.1.1
       util-deprecate: 1.0.2
-    dev: true
 
   /readable-stream@3.6.2:
     resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
@@ -4945,9 +4999,6 @@ packages:
     resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
     dev: true
 
-  /resolve-alpn@1.2.1:
-    resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
-
   /resolve-cwd@3.0.0:
     resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
     engines: {node: '>=8'}
@@ -4976,10 +5027,10 @@ packages:
       path-parse: 1.0.7
       supports-preserve-symlinks-flag: 1.0.0
 
-  /responselike@2.0.1:
-    resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
+  /responselike@1.0.2:
+    resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==}
     dependencies:
-      lowercase-keys: 2.0.0
+      lowercase-keys: 1.0.1
 
   /retry@0.12.0:
     resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
@@ -5027,7 +5078,6 @@ packages:
 
   /safe-buffer@5.1.2:
     resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
-    dev: true
 
   /safe-buffer@5.2.1:
     resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@@ -5170,8 +5220,8 @@ packages:
     dev: true
     optional: true
 
-  /source-map-js@1.1.0:
-    resolution: {integrity: sha512-9vC2SfsJzlej6MAaMPLu8HiBSHGdRAJ9hVFYN1ibZoNkeanmDmLUcIrj6G9DGL7XMJ54AKg/G75akXl1/izTOw==}
+  /source-map-js@1.2.0:
+    resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
     engines: {node: '>=0.10.0'}
     dev: true
 
@@ -5255,7 +5305,6 @@ packages:
     resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
     dependencies:
       safe-buffer: 5.1.2
-    dev: true
 
   /string_decoder@1.3.0:
     resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
@@ -5369,11 +5418,11 @@ packages:
       normalize-path: 3.0.0
       object-hash: 3.0.0
       picocolors: 1.0.0
-      postcss: 8.4.36
-      postcss-import: 15.1.0(postcss@8.4.36)
-      postcss-js: 4.0.1(postcss@8.4.36)
-      postcss-load-config: 4.0.1(postcss@8.4.36)
-      postcss-nested: 6.0.1(postcss@8.4.36)
+      postcss: 8.4.37
+      postcss-import: 15.1.0(postcss@8.4.37)
+      postcss-js: 4.0.1(postcss@8.4.37)
+      postcss-load-config: 4.0.1(postcss@8.4.37)
+      postcss-nested: 6.0.1(postcss@8.4.37)
       postcss-selector-parser: 6.0.13
       resolve: 1.22.2
       sucrase: 3.34.0
@@ -5496,6 +5545,10 @@ packages:
     engines: {node: '>=4'}
     dev: true
 
+  /to-readable-stream@1.0.0:
+    resolution: {integrity: sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==}
+    engines: {node: '>=6'}
+
   /to-regex-range@5.0.1:
     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
     engines: {node: '>=8.0'}
@@ -5553,7 +5606,7 @@ packages:
       '@babel/core': 7.21.8
       bs-logger: 0.2.6
       fast-json-stable-stringify: 2.1.0
-      jest: 29.7.0(@types/node@20.11.28)
+      jest: 29.7.0(@types/node@20.11.30)
       jest-util: 29.7.0
       json5: 2.2.3
       lodash.memoize: 4.1.2
@@ -5579,6 +5632,12 @@ packages:
       webpack: 5.90.3(webpack-cli@5.1.4)
     dev: false
 
+  /tunnel@0.0.6:
+    resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
+    engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
+    requiresBuild: true
+    optional: true
+
   /type-detect@4.0.8:
     resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
     engines: {node: '>=4'}
@@ -5595,6 +5654,9 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /typedarray@0.0.6:
+    resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
+
   /typescript@4.9.5:
     resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
     engines: {node: '>=4.2.0'}
@@ -5649,6 +5711,12 @@ packages:
     dependencies:
       punycode: 2.3.0
 
+  /url-parse-lax@3.0.0:
+    resolution: {integrity: sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==}
+    engines: {node: '>=4'}
+    dependencies:
+      prepend-http: 2.0.0
+
   /url-parse@1.5.10:
     resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
     dependencies:
@@ -5662,7 +5730,6 @@ packages:
 
   /util-deprecate@1.0.2:
     resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
-    dev: true
 
   /v8-to-istanbul@9.1.0:
     resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==}
diff --git a/src/typesGithub.ts b/src/typesGithub.ts
index 49f3354f..8649b543 100644
--- a/src/typesGithub.ts
+++ b/src/typesGithub.ts
@@ -245,6 +245,22 @@ export interface IssueComments {
   body: string;
 }
 
+export interface ReleaseComments {
+  url: string;
+  assets_url: string;
+  html_url: string;
+  id: number;
+  author: User;
+  node_id: string;
+  tag_name: string;
+  name: string;
+  draft: boolean;
+  prerelease: boolean;
+  created_at: string;
+  published_at: string;
+  body: string;
+}
+
 export interface GraphQLSearch<T> {
   data: {
     data: {
diff --git a/src/utils/subject.ts b/src/utils/subject.ts
index a1c9e7cd..e4d2a051 100644
--- a/src/utils/subject.ts
+++ b/src/utils/subject.ts
@@ -9,6 +9,7 @@ import {
   Notification,
   PullRequest,
   PullRequestStateType,
+  ReleaseComments,
   User,
   WorkflowRunAttributes,
 } from '../typesGithub';
@@ -27,6 +28,8 @@ export async function getGitifySubjectDetails(
       return await getGitifySubjectForIssue(notification, token);
     case 'PullRequest':
       return await getGitifySubjectForPullRequest(notification, token);
+    case 'Release':
+      return await getGitifySubjectForRelease(notification, token);
     case 'WorkflowRun':
       return getGitifySubjectForWorkflowRun(notification);
     default:
@@ -156,6 +159,18 @@ async function getGitifySubjectForPullRequest(
   };
 }
 
+async function getGitifySubjectForRelease(
+  notification: Notification,
+  token: string,
+): Promise<GitifySubject> {
+  const releaseCommentUser = await getLatestCommentUser(notification, token);
+
+  return {
+    state: null,
+    user: releaseCommentUser.login,
+  };
+}
+
 function getGitifySubjectForWorkflowRun(
   notification: Notification,
 ): GitifySubject {
@@ -203,9 +218,11 @@ async function getLatestCommentUser(
   notification: Notification,
   token: string,
 ): Promise<User> {
-  const response: IssueComments = (
+  const response: IssueComments | ReleaseComments = (
     await apiRequestAuth(notification.subject.latest_comment_url, 'GET', token)
   ).data;
 
-  return response.user;
+  return (
+    (response as IssueComments).user ?? (response as ReleaseComments).author
+  );
 }


Test patch:
diff --git a/src/components/AllRead.test.tsx b/src/components/AllRead.test.tsx
index e262e641..fc3e0655 100644
--- a/src/components/AllRead.test.tsx
+++ b/src/components/AllRead.test.tsx
@@ -5,7 +5,7 @@ import { mockMathRandom } from './test-utils';
 
 import { AllRead } from './AllRead';
 
-describe('components/all-read.tsx', function () {
+describe('components/AllRead.tsx', function () {
   // The read emoji randomly rotates, but then the snapshots would never match
   // Have to make it consistent so the emojis are always the same
   mockMathRandom(0.1);
diff --git a/src/components/Loading.test.tsx b/src/components/Loading.test.tsx
index 21b06474..5e96865f 100644
--- a/src/components/Loading.test.tsx
+++ b/src/components/Loading.test.tsx
@@ -12,7 +12,7 @@ jest.mock('nprogress', () => ({
   remove: jest.fn(),
 }));
 
-describe('components/Loading.js', () => {
+describe('components/Loading.tsx', () => {
   beforeEach(() => {
     jest.clearAllMocks();
   });
diff --git a/src/components/Logo.test.tsx b/src/components/Logo.test.tsx
index 2821b090..10ab08db 100644
--- a/src/components/Logo.test.tsx
+++ b/src/components/Logo.test.tsx
@@ -4,7 +4,7 @@ import { fireEvent, render } from '@testing-library/react';
 
 import { Logo } from './Logo';
 
-describe('components/ui/logo.tsx', () => {
+describe('components/Logo.tsx', () => {
   it('renders correctly (light)', () => {
     const tree = TestRenderer.create(<Logo />);
 
diff --git a/src/components/NotificationRow.test.tsx b/src/components/NotificationRow.test.tsx
index 9158a62d..035a7722 100644
--- a/src/components/NotificationRow.test.tsx
+++ b/src/components/NotificationRow.test.tsx
@@ -9,7 +9,7 @@ import { mockedSingleNotification } from '../__mocks__/mockedData';
 import { NotificationRow } from './NotificationRow';
 import { mockAccounts, mockSettings } from '../__mocks__/mock-state';
 
-describe('components/Notification.js', () => {
+describe('components/NotificationRow.tsx', () => {
   beforeEach(() => {
     jest.spyOn(helpers, 'openInBrowser');
   });
diff --git a/src/components/Oops.test.tsx b/src/components/Oops.test.tsx
index 816fa702..68aa3ee7 100644
--- a/src/components/Oops.test.tsx
+++ b/src/components/Oops.test.tsx
@@ -5,7 +5,7 @@ import { mockMathRandom } from './test-utils';
 
 import { Oops } from './Oops';
 
-describe('components/oops.tsx', function () {
+describe('components/Oops.tsx', function () {
   // The error emoji randomly rotates, but then the snapshots would never match
   // Have to make it consistent so the emojis are always the same
   mockMathRandom(0.1);
diff --git a/src/components/__snapshots__/AccountNotifications.test.tsx.snap b/src/components/__snapshots__/AccountNotifications.test.tsx.snap
index 1e14664e..7bb967d1 100644
--- a/src/components/__snapshots__/AccountNotifications.test.tsx.snap
+++ b/src/components/__snapshots__/AccountNotifications.test.tsx.snap
@@ -12,7 +12,6 @@ exports[`components/AccountNotifications.tsx should render itself (github.com wi
       fill="currentColor"
       focusable="false"
       height={20}
-      role="img"
       style={
         {
           "display": "inline-block",
@@ -46,7 +45,6 @@ exports[`components/AccountNotifications.tsx should render itself (github.com wi
     fill="currentColor"
     focusable="false"
     height={20}
-    role="img"
     style={
       {
         "display": "inline-block",
diff --git a/src/components/__snapshots__/AllRead.test.tsx.snap b/src/components/__snapshots__/AllRead.test.tsx.snap
index 704062fd..b1874989 100644
--- a/src/components/__snapshots__/AllRead.test.tsx.snap
+++ b/src/components/__snapshots__/AllRead.test.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`components/all-read.tsx should render itself & its children 1`] = `
+exports[`components/AllRead.tsx should render itself & its children 1`] = `
 <div
   className="flex flex-1 flex-col justify-center items-center p-4 bg-white dark:bg-gray-dark text-black dark:text-white"
 >
diff --git a/src/components/__snapshots__/Logo.test.tsx.snap b/src/components/__snapshots__/Logo.test.tsx.snap
index 96060c7d..635657bb 100644
--- a/src/components/__snapshots__/Logo.test.tsx.snap
+++ b/src/components/__snapshots__/Logo.test.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`components/ui/logo.tsx renders correctly (light) 1`] = `
+exports[`components/Logo.tsx renders correctly (light) 1`] = `
 <svg
   className=""
   onClick={[Function]}
@@ -47,7 +47,7 @@ exports[`components/ui/logo.tsx renders correctly (light) 1`] = `
 </svg>
 `;
 
-exports[`components/ui/logo.tsx renders correctly(dark) 1`] = `
+exports[`components/Logo.tsx renders correctly(dark) 1`] = `
 <svg
   className=""
   onClick={[Function]}
diff --git a/src/components/__snapshots__/NotificationRow.test.tsx.snap b/src/components/__snapshots__/NotificationRow.test.tsx.snap
index 0f107e1f..b671daa4 100644
--- a/src/components/__snapshots__/NotificationRow.test.tsx.snap
+++ b/src/components/__snapshots__/NotificationRow.test.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`components/Notification.js should render itself & its children 1`] = `
+exports[`components/NotificationRow.tsx should render itself & its children 1`] = `
 <div
   className="flex space-x-3 py-2 px-3 bg-white dark:bg-gray-dark dark:text-white hover:bg-gray-100 dark:hover:bg-gray-darker border-b border-gray-100 dark:border-gray-darker group"
 >
@@ -9,7 +9,6 @@ exports[`components/Notification.js should render itself & its children 1`] = `
     title="Open Issue"
   >
     <svg
-      aria-hidden="false"
       aria-label="Issue"
       className="octicon octicon-issue-opened"
       fill="currentColor"
@@ -72,7 +71,6 @@ exports[`components/Notification.js should render itself & its children 1`] = `
       title="Mark as Done"
     >
       <svg
-        aria-hidden="false"
         aria-label="Mark as Done"
         className="octicon octicon-check"
         fill="currentColor"
@@ -101,7 +99,6 @@ exports[`components/Notification.js should render itself & its children 1`] = `
       title="Unsubscribe"
     >
       <svg
-        aria-hidden="false"
         aria-label="Unsubscribe"
         className="octicon octicon-bell-slash"
         fill="currentColor"
@@ -130,7 +127,6 @@ exports[`components/Notification.js should render itself & its children 1`] = `
       title="Mark as Read"
     >
       <svg
-        aria-hidden="false"
         aria-label="Mark as Read"
         className="octicon octicon-read"
         fill="currentColor"
diff --git a/src/components/__snapshots__/Oops.test.tsx.snap b/src/components/__snapshots__/Oops.test.tsx.snap
index c6ef9f63..661f8d20 100644
--- a/src/components/__snapshots__/Oops.test.tsx.snap
+++ b/src/components/__snapshots__/Oops.test.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`components/oops.tsx should render itself & its children 1`] = `
+exports[`components/Oops.tsx should render itself & its children 1`] = `
 <div
   className="flex flex-1 flex-col justify-center items-center p-4 bg-white dark:bg-gray-dark text-black dark:text-white"
 >
diff --git a/src/components/__snapshots__/Repository.test.tsx.snap b/src/components/__snapshots__/Repository.test.tsx.snap
index a7059bbf..cacb209b 100644
--- a/src/components/__snapshots__/Repository.test.tsx.snap
+++ b/src/components/__snapshots__/Repository.test.tsx.snap
@@ -27,7 +27,6 @@ exports[`components/Repository.tsx should render itself & its children 1`] = `
         title="Mark Repository as Done"
       >
         <svg
-          aria-hidden="false"
           aria-label="Mark Repository as Done"
           className="octicon octicon-check"
           fill="currentColor"
@@ -59,7 +58,6 @@ exports[`components/Repository.tsx should render itself & its children 1`] = `
         title="Mark Repository as Read"
       >
         <svg
-          aria-hidden="false"
           aria-label="Mark Repository as Read"
           className="octicon octicon-read"
           fill="currentColor"
diff --git a/src/components/__snapshots__/Sidebar.test.tsx.snap b/src/components/__snapshots__/Sidebar.test.tsx.snap
index 8db67adc..11a9935a 100644
--- a/src/components/__snapshots__/Sidebar.test.tsx.snap
+++ b/src/components/__snapshots__/Sidebar.test.tsx.snap
@@ -65,7 +65,6 @@ exports[`components/Sidebar.tsx should render itself & its children (logged in)
       title="4 Unread Notifications"
     >
       <svg
-        aria-hidden="false"
         aria-label="4 Unread Notifications"
         className="octicon octicon-bell"
         fill="currentColor"
@@ -194,7 +193,6 @@ exports[`components/Sidebar.tsx should render itself & its children (logged out)
       title="4 Unread Notifications"
     >
       <svg
-        aria-hidden="false"
         aria-label="4 Unread Notifications"
         className="octicon octicon-bell"
         fill="currentColor"
diff --git a/src/routes/LoginEnterprise.test.tsx b/src/routes/LoginEnterprise.test.tsx
index f6c31c0f..64c69f7d 100644
--- a/src/routes/LoginEnterprise.test.tsx
+++ b/src/routes/LoginEnterprise.test.tsx
@@ -16,7 +16,7 @@ jest.mock('react-router-dom', () => ({
   useNavigate: () => mockNavigate,
 }));
 
-describe('routes/LoginEnterprise.js', () => {
+describe('routes/LoginEnterprise.tsx', () => {
   const mockAccounts: AuthState = {
     enterpriseAccounts: [],
     user: null,
diff --git a/src/routes/LoginWithToken.test.tsx b/src/routes/LoginWithToken.test.tsx
index a1e0d0eb..1e34d288 100644
--- a/src/routes/LoginWithToken.test.tsx
+++ b/src/routes/LoginWithToken.test.tsx
@@ -13,7 +13,7 @@ jest.mock('react-router-dom', () => ({
   useNavigate: () => mockNavigate,
 }));
 
-describe('routes/LoginWithToken.js', () => {
+describe('routes/LoginWithToken.tsx', () => {
   const openExternalMock = jest.spyOn(shell, 'openExternal');
 
   const mockValidateToken = jest.fn();
diff --git a/src/routes/Notifications.test.tsx b/src/routes/Notifications.test.tsx
index 89e37d7a..9f7f8ee6 100644
--- a/src/routes/Notifications.test.tsx
+++ b/src/routes/Notifications.test.tsx
@@ -17,7 +17,7 @@ jest.mock('../components/Oops', () => ({
   Oops: 'Oops',
 }));
 
-describe('routes/Notifications.ts', () => {
+describe('routes/Notifications.tsx', () => {
   it('should render itself & its children (with notifications)', () => {
     const tree = TestRenderer.create(
       <AppContext.Provider
diff --git a/src/routes/__snapshots__/LoginEnterprise.test.tsx.snap b/src/routes/__snapshots__/LoginEnterprise.test.tsx.snap
index 8e139ef8..43d74e23 100644
--- a/src/routes/__snapshots__/LoginEnterprise.test.tsx.snap
+++ b/src/routes/__snapshots__/LoginEnterprise.test.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`routes/LoginEnterprise.js renders correctly 1`] = `
+exports[`routes/LoginEnterprise.tsx renders correctly 1`] = `
 <div
   className="flex-1 bg-white dark:bg-gray-dark dark:text-white"
 >
@@ -13,7 +13,6 @@ exports[`routes/LoginEnterprise.js renders correctly 1`] = `
       title="Go Back"
     >
       <svg
-        aria-hidden="false"
         aria-label="Go Back"
         className="hover:text-gray-400"
         fill="currentColor"
diff --git a/src/routes/__snapshots__/LoginWithToken.test.tsx.snap b/src/routes/__snapshots__/LoginWithToken.test.tsx.snap
index b3532293..8be1db86 100644
--- a/src/routes/__snapshots__/LoginWithToken.test.tsx.snap
+++ b/src/routes/__snapshots__/LoginWithToken.test.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`routes/LoginWithToken.js renders correctly 1`] = `
+exports[`routes/LoginWithToken.tsx renders correctly 1`] = `
 <div
   className="flex-1 bg-white dark:bg-gray-dark dark:text-white"
 >
@@ -13,7 +13,6 @@ exports[`routes/LoginWithToken.js renders correctly 1`] = `
       title="Go Back"
     >
       <svg
-        aria-hidden="false"
         aria-label="Go Back"
         className="hover:text-gray-400"
         fill="currentColor"
diff --git a/src/routes/__snapshots__/Notifications.test.tsx.snap b/src/routes/__snapshots__/Notifications.test.tsx.snap
index 1f16aa3d..ab3a6744 100644
--- a/src/routes/__snapshots__/Notifications.test.tsx.snap
+++ b/src/routes/__snapshots__/Notifications.test.tsx.snap
@@ -1,10 +1,10 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`routes/Notifications.ts should render itself & its children (all read notifications) 1`] = `<AllRead />`;
+exports[`routes/Notifications.tsx should render itself & its children (all read notifications) 1`] = `<AllRead />`;
 
-exports[`routes/Notifications.ts should render itself & its children (error page - oops) 1`] = `<Oops />`;
+exports[`routes/Notifications.tsx should render itself & its children (error page - oops) 1`] = `<Oops />`;
 
-exports[`routes/Notifications.ts should render itself & its children (with notifications) 1`] = `
+exports[`routes/Notifications.tsx should render itself & its children (with notifications) 1`] = `
 <div
   className="flex flex-col flex-1 bg-white dark:bg-gray-dark"
 >
diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap
index c0c3c21e..e6d996a6 100644
--- a/src/routes/__snapshots__/Settings.test.tsx.snap
+++ b/src/routes/__snapshots__/Settings.test.tsx.snap
@@ -41,7 +41,6 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
       title="Go Back"
     >
       <svg
-        aria-hidden="false"
         aria-label="Go Back"
         class="hover:text-gray-400"
         fill="currentColor"
diff --git a/src/utils/__snapshots__/github-api.test.ts.snap b/src/utils/__snapshots__/github-api.test.ts.snap
index 2ad7e8e9..b12b53e8 100644
--- a/src/utils/__snapshots__/github-api.test.ts.snap
+++ b/src/utils/__snapshots__/github-api.test.ts.snap
@@ -1,143 +1,143 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`formatReason should format the notification reason 1`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 1`] = `
 {
   "description": "You were requested to review and approve a deployment.",
   "type": "Approval Requested",
 }
 `;
 
-exports[`formatReason should format the notification reason 2`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 2`] = `
 {
   "description": "You were assigned to the issue.",
   "type": "Assign",
 }
 `;
 
-exports[`formatReason should format the notification reason 3`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 3`] = `
 {
   "description": "You created the thread.",
   "type": "Author",
 }
 `;
 
-exports[`formatReason should format the notification reason 4`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 4`] = `
 {
   "description": "A GitHub Actions workflow run was triggered for your repository",
   "type": "Workflow Run",
 }
 `;
 
-exports[`formatReason should format the notification reason 5`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 5`] = `
 {
   "description": "You commented on the thread.",
   "type": "Comment",
 }
 `;
 
-exports[`formatReason should format the notification reason 6`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 6`] = `
 {
   "description": "You accepted an invitation to contribute to the repository.",
   "type": "Invitation",
 }
 `;
 
-exports[`formatReason should format the notification reason 7`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 7`] = `
 {
   "description": "You subscribed to the thread (via an issue or pull request).",
   "type": "Manual",
 }
 `;
 
-exports[`formatReason should format the notification reason 8`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 8`] = `
 {
   "description": "Organization members have requested to enable a feature such as Draft Pull Requests or CoPilot.",
   "type": "Member Feature Requested",
 }
 `;
 
-exports[`formatReason should format the notification reason 9`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 9`] = `
 {
   "description": "You were specifically @mentioned in the content.",
   "type": "Mention",
 }
 `;
 
-exports[`formatReason should format the notification reason 10`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 10`] = `
 {
   "description": "You, or a team you're a member of, were requested to review a pull request.",
   "type": "Review Requested",
 }
 `;
 
-exports[`formatReason should format the notification reason 11`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 11`] = `
 {
   "description": "You were credited for contributing to a security advisory.",
   "type": "Security Advisory Credit",
 }
 `;
 
-exports[`formatReason should format the notification reason 12`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 12`] = `
 {
   "description": "GitHub discovered a security vulnerability in your repository.",
   "type": "Security Alert",
 }
 `;
 
-exports[`formatReason should format the notification reason 13`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 13`] = `
 {
   "description": "You changed the thread state (for example, closing an issue or merging a pull request).",
   "type": "State Change",
 }
 `;
 
-exports[`formatReason should format the notification reason 14`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 14`] = `
 {
   "description": "You're watching the repository.",
   "type": "Subscribed",
 }
 `;
 
-exports[`formatReason should format the notification reason 15`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 15`] = `
 {
   "description": "You were on a team that was mentioned.",
   "type": "Team Mention",
 }
 `;
 
-exports[`formatReason should format the notification reason 16`] = `
+exports[`utils/github-api.ts formatReason - should format the notification reason 16`] = `
 {
   "description": "The reason for this notification is not supported by the app.",
   "type": "Unknown",
 }
 `;
 
-exports[`getNotificationTypeIconColor should format the notification color for check suite 1`] = `"text-gray-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for check suite 1`] = `"text-gray-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for check suite 2`] = `"text-red-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for check suite 2`] = `"text-red-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for check suite 3`] = `"text-gray-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for check suite 3`] = `"text-gray-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for check suite 4`] = `"text-green-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for check suite 4`] = `"text-green-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for check suite 5`] = `"text-gray-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for check suite 5`] = `"text-gray-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 1`] = `"text-green-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 1`] = `"text-green-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 2`] = `"text-red-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 2`] = `"text-red-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 3`] = `"text-purple-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 3`] = `"text-purple-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 4`] = `"text-gray-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 4`] = `"text-gray-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 5`] = `"text-purple-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 5`] = `"text-purple-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 6`] = `"text-gray-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 6`] = `"text-gray-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 7`] = `"text-green-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 7`] = `"text-green-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 8`] = `"text-green-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 8`] = `"text-green-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 9`] = `"text-purple-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 9`] = `"text-purple-500"`;
 
-exports[`getNotificationTypeIconColor should format the notification color for state 10`] = `"text-gray-500"`;
+exports[`utils/github-api.ts getNotificationTypeIconColor should format the notification color for state 10`] = `"text-gray-500"`;
diff --git a/src/utils/appearance.test.ts b/src/utils/appearance.test.ts
index a9d7f0b0..a6f8005e 100644
--- a/src/utils/appearance.test.ts
+++ b/src/utils/appearance.test.ts
@@ -3,7 +3,7 @@ import { setAppearance } from './appearance';
 
 import * as appearanceHelpers from './appearance';
 
-describe('utils/appearance.tsx', () => {
+describe('utils/appearance.ts', () => {
   beforeAll(() => {
     jest.spyOn(appearanceHelpers, 'setLightMode');
     jest.spyOn(appearanceHelpers, 'setDarkMode');
diff --git a/src/utils/github-api.test.ts b/src/utils/github-api.test.ts
index dbb5fe92..5a3cffc9 100644
--- a/src/utils/github-api.test.ts
+++ b/src/utils/github-api.test.ts
@@ -5,8 +5,8 @@ import {
 } from './github-api';
 import { Reason, StateType, Subject, SubjectType } from '../typesGithub';
 
-describe('formatReason', () => {
-  it('should format the notification reason', () => {
+describe('utils/github-api.ts', () => {
+  it('formatReason - should format the notification reason', () => {
     expect(formatReason('approval_requested')).toMatchSnapshot();
     expect(formatReason('assign')).toMatchSnapshot();
     expect(formatReason('author')).toMatchSnapshot();
@@ -24,10 +24,8 @@ describe('formatReason', () => {
     expect(formatReason('team_mention')).toMatchSnapshot();
     expect(formatReason('something_else_unknown' as Reason)).toMatchSnapshot();
   });
-});
 
-describe('getNotificationTypeIcon', () => {
-  it('should get the notification type icon', () => {
+  describe('getNotificationTypeIcon - should get the notification type icon', () => {
     expect(
       getNotificationTypeIcon(
         createSubjectMock({ type: 'CheckSuite', state: null }),
@@ -188,88 +186,90 @@ describe('getNotificationTypeIcon', () => {
       'QuestionIcon',
     );
   });
-});
 
-describe('getNotificationTypeIconColor', () => {
-  it('should format the notification color for check suite', () => {
-    expect(
-      getNotificationTypeIconColor(
-        createSubjectMock({
-          type: 'CheckSuite',
-          state: 'cancelled',
-        }),
-      ),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(
-        createSubjectMock({
-          type: 'CheckSuite',
-          state: 'failure',
-        }),
-      ),
-    ).toMatchSnapshot();
+  describe('getNotificationTypeIconColor', () => {
+    it('should format the notification color for check suite', () => {
+      expect(
+        getNotificationTypeIconColor(
+          createSubjectMock({
+            type: 'CheckSuite',
+            state: 'cancelled',
+          }),
+        ),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(
+          createSubjectMock({
+            type: 'CheckSuite',
+            state: 'failure',
+          }),
+        ),
+      ).toMatchSnapshot();
 
-    expect(
-      getNotificationTypeIconColor(
-        createSubjectMock({
-          type: 'CheckSuite',
-          state: 'skipped',
-        }),
-      ),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(
-        createSubjectMock({
-          type: 'CheckSuite',
-          state: 'success',
-        }),
-      ),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(
-        createSubjectMock({
-          type: 'CheckSuite',
-          state: null,
-        }),
-      ),
-    ).toMatchSnapshot();
-  });
+      expect(
+        getNotificationTypeIconColor(
+          createSubjectMock({
+            type: 'CheckSuite',
+            state: 'skipped',
+          }),
+        ),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(
+          createSubjectMock({
+            type: 'CheckSuite',
+            state: 'success',
+          }),
+        ),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(
+          createSubjectMock({
+            type: 'CheckSuite',
+            state: null,
+          }),
+        ),
+      ).toMatchSnapshot();
+    });
 
-  it('should format the notification color for state', () => {
-    expect(
-      getNotificationTypeIconColor(createSubjectMock({ state: 'ANSWERED' })),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(createSubjectMock({ state: 'closed' })),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(createSubjectMock({ state: 'completed' })),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(createSubjectMock({ state: 'draft' })),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(createSubjectMock({ state: 'merged' })),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(createSubjectMock({ state: 'not_planned' })),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(createSubjectMock({ state: 'open' })),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(createSubjectMock({ state: 'reopened' })),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(createSubjectMock({ state: 'RESOLVED' })),
-    ).toMatchSnapshot();
-    expect(
-      getNotificationTypeIconColor(
-        createSubjectMock({
-          state: 'something_else_unknown' as StateType,
-        }),
-      ),
-    ).toMatchSnapshot();
+    it('should format the notification color for state', () => {
+      expect(
+        getNotificationTypeIconColor(createSubjectMock({ state: 'ANSWERED' })),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(createSubjectMock({ state: 'closed' })),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(createSubjectMock({ state: 'completed' })),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(createSubjectMock({ state: 'draft' })),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(createSubjectMock({ state: 'merged' })),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(
+          createSubjectMock({ state: 'not_planned' }),
+        ),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(createSubjectMock({ state: 'open' })),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(createSubjectMock({ state: 'reopened' })),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(createSubjectMock({ state: 'RESOLVED' })),
+      ).toMatchSnapshot();
+      expect(
+        getNotificationTypeIconColor(
+          createSubjectMock({
+            state: 'something_else_unknown' as StateType,
+          }),
+        ),
+      ).toMatchSnapshot();
+    });
   });
 });
 
diff --git a/src/utils/remove-notifications.test.ts b/src/utils/remove-notifications.test.ts
index 5c9da500..0ec62a90 100644
--- a/src/utils/remove-notifications.test.ts
+++ b/src/utils/remove-notifications.test.ts
@@ -5,7 +5,7 @@ import {
 } from '../__mocks__/mockedData';
 import { removeNotifications } from './remove-notifications';
 
-describe('utils/remove-notification.ts', () => {
+describe('utils/remove-notifications.ts', () => {
   const repoSlug = mockedSingleNotification.repository.full_name;
   const hostname = mockedSingleAccountNotifications[0].hostname;
 
diff --git a/src/utils/subject.test.ts b/src/utils/subject.test.ts
index b4c659af..b2dc8597 100644
--- a/src/utils/subject.test.ts
+++ b/src/utils/subject.test.ts
@@ -9,7 +9,7 @@ import {
   getWorkflowRunAttributes,
 } from './subject';
 import { SubjectType } from '../typesGithub';
-describe('utils/state.ts', () => {
+describe('utils/subject.ts', () => {
   beforeEach(() => {
     // axios will default to using the XHR adapter which can't be intercepted
     // by nock. So, configure axios to use the node adapter.
@@ -592,6 +592,32 @@ describe('utils/state.ts', () => {
     });
   });
 
+  describe('getGitifySubjectForRelease', () => {
+    it('release notification', async () => {
+      const mockNotification = {
+        ...mockedSingleNotification,
+        subject: {
+          ...mockedSingleNotification.subject,
+          type: 'Release' as SubjectType,
+          url: 'https://api.github.com/repos/manosim/notifications-test/releases/1',
+          latest_comment_url:
+            'https://api.github.com/repos/manosim/notifications-test/releases/1',
+        },
+      };
+
+      nock('https://api.github.com')
+        .get('/repos/manosim/notifications-test/releases/1')
+        .reply(200, { author: { login: 'some-user' } });
+
+      const result = await getGitifySubjectDetails(
+        mockNotification,
+        mockAccounts.token,
+      );
+
+      expect(result.user).toBe('some-user');
+    });
+  });
+
   describe('getWorkflowRunState', () => {
     it('deploy review workflow run state', async () => {
       const mockNotification = {