diff --git a/Dockerfile b/Dockerfile
index 9e1381eb45..d7cf130a7a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -48,7 +48,7 @@ FROM docker.io/node:20-slim
 WORKDIR /firefish
 
 # Install runtime dependencies
-RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get install -y --no-install-recommends zip unzip tini ffmpeg ca-certificates
+RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get install -y --no-install-recommends zip unzip tini ffmpeg ca-certificates curl
 
 RUN echo 'deb https://deb.debian.org/debian experimental main' | tee /etc/apt/sources.list
 RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get --target-release experimental install -y --no-install-recommends libc6
diff --git a/docker-compose.example.yml b/docker-compose.example.yml
index c2c60786d9..aaad55a158 100644
--- a/docker-compose.example.yml
+++ b/docker-compose.example.yml
@@ -6,8 +6,10 @@ services:
     container_name: firefish_web
     restart: unless-stopped
     depends_on:
-      - db
-      - redis
+      db:
+        condition: service_healthy
+      redis:
+        condition: service_healthy
     ports:
       - "3000:3000"
     networks:
@@ -19,6 +21,15 @@ services:
       - ./custom:/firefish/custom:ro
       - ./files:/firefish/files
       - ./.config:/firefish/.config:ro
+    healthcheck:
+      test: curl -f http://localhost:3000 || exit 1
+      interval: 5s
+      timeout: 5s
+      retries: 5
+    deploy:
+      resources:
+        limits:
+          memory: 4096M
 
   redis:
     restart: unless-stopped
@@ -28,6 +39,16 @@ services:
       - calcnet
     volumes:
       - ./redis:/data
+    healthcheck:
+      test: redis-cli ping
+      interval: 5s
+      timeout: 5s
+      retries: 5
+  #  deploy:
+  #    resources:
+  #      limits:
+  #        memory: 200M
+
 
   db:
     restart: unless-stopped
@@ -39,6 +60,15 @@ services:
       - .config/docker.env
     volumes:
       - ./db:/var/lib/postgresql/data
+    healthcheck:
+      test: pg_isready --user="$${POSTGRES_USER}" --dbname="$${POSTGRES_DB}"
+      interval: 5s
+      timeout: 5s
+      retries: 5
+  #  deploy:
+  #    resources:
+  #      limits:
+  #        memory: 200M
 
 networks:
   calcnet:
diff --git a/docs/api-change.md b/docs/api-change.md
index 1fac31a7a9..7bab3af1b3 100644
--- a/docs/api-change.md
+++ b/docs/api-change.md
@@ -4,6 +4,7 @@ Breaking changes are indicated by the :warning: icon.
 
 ## Unreleased
 
+- :warning: `followingCount` and `followersCount` in `users/show` will be `null` (instead of 0) if these values are unavailable.
 - :warning: `admin/search/index-all` is removed since posts are now indexed automatically.
 - New optional parameters are added to `notes/search` endpoint:
 	- `sinceDate`
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index f38779bda4..9eb758a74d 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -323,8 +323,8 @@ _2fa:
   securityKeyInfo: A més de l'autenticació d'empremta digital o PIN, també podeu configurar
     l'autenticació mitjançant claus de seguretat de maquinari compatibles amb FIDO2
     per protegir encara més el vostre compte.
-  step4: A partir d'ara, qualsevol intent d'inici de sessió futur demanarà aquest
-    token d'inici de sessió.
+  step4: A partir d'ara, qualsevol intent d'inici de sessió futur demanarà aquesta
+    clau d'inici de sessió.
   registerSecurityKey: Registrar una clau de seguretat o d'accés
   step1: En primer lloc, instal·la una aplicació d'autenticació (com ara {a} o {b})
     al dispositiu.
@@ -2067,9 +2067,8 @@ _relayStatus:
 deleted: Eliminat
 editNote: Edita la publicació
 edited: 'Editat el {date} {time}'
-signupsDisabled: Actualment, les inscripcions en aquest servidor estan desactivades,
-  però sempre podeu registrar-vos en un altre servidor. Si teniu un codi d'invitació
-  per a aquest servidor, introduïu-lo a continuació.
+signupsDisabled: Actualment, les inscripcions en aquest servidor estan desactivades.
+  Si teniu un codi d'invitació per a aquest servidor, introduïu-lo a continuació.
 userSaysSomethingReasonQuote: '{name} ha citat una publicació que conté {reason}'
 userSaysSomethingReasonReply: '{name} ha respost a una publicació que conté {reason}'
 userSaysSomethingReasonRenote: '{name} ha impulsat una publicació que conté {reason}'
@@ -2253,7 +2252,34 @@ searchWordsDescription: "Per cercar publicacions, escriu el terme a buscar. Sepa
   o la ID en aquest camp i fes clic al botó 'Trobar'. Fent clic a 'Cercar' trobarà
   publicacions que, literalment , continguin la ID/adreça URL."
 searchPostsWithFiles: Només publicacions amb fitxers
-searchCwAndAlt: Inclou avisos de contingut i arxius amb descripcions.
+searchCwAndAlt: Inclou avisos de contingut i arxius amb descripcions
 searchUsers: Publicat per (opcional)
 searchRange: Publicat dintre de (opcional)
 publishTimelines: Publica línies de temps per visitants
+toPost: Publicà
+publishTimelinesDescription: Si està activat, les línies de temps Global i Local es
+  mostraran a {url} fins i tot sense estar registrat.
+noAltTextWarning: Alguns fitxers adjunts no tenen una descripció. T'has s oblidat
+  d'escriure-les?
+showNoAltTextWarning: Mostra un avís si públiques un fitxer sense descripció
+toReply: Respon
+toQuote: Cita
+toEdit: Edita
+searchUsersDescription: "Per buscar publicacions concretes d'un usuari/servidor, escriu
+  la ID (@usuari@exemple.com, o @usuari per un usuari local) o nom del domini (exemple.com).\n
+  \nSi escrius 'me' (sense cometes), totes les teves publicacions (incloent-hi publicacions
+  sense llistar, només per a seguidors i secretes) es buscaran.\n\nSi escrius 'local'
+  (sense cometes), el resultat serà filtrat per mostrar només publicacions d'aquest
+  servidor."
+messagingUnencryptedInfo: Els xats a Firefish no són encriptats d'extrem a extrem.
+  No comparteixis dades sensibles fent servir Firefish.
+searchRangeDescription: "Si vols filtrar per un període de temps, has de fer servir
+  aquest format: 20220615-20231031\n\nSi no escrius l'any (per exemple 0105-0106 o
+  20231105-0110), serà interpretat com l'any en curs.\n\nInclús pots morir la data
+  de començament o de finalització. Per exemple, -0102 filtrarà els resultats per
+  mostrar només publicacions fetes abans del 2 de gener d'aquest any, i 20231026-
+  filtrarà els resultats per mostrar publicacions fetes després del 26 d'octubre del
+  2023."
+moderationNote: Nota de moderació
+ipFirstAcknowledged: Data en què es va veure la adreça IP per primera vegada
+driveCapacityOverride: Capacitat del disc esgotada
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 44e8fb1d7c..df6e57ba5f 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -2218,3 +2218,4 @@ renotes: Boosts
 quotes: Zitate
 moreUrlsDescription: "Die Seiten, welche angepinnt werde sollen, im Hilfe-Menü in
   der unteren linken Ecke in folgender Notation angeben:\n\"Anzeigename\": https://example.com/"
+toQuote: Zitat
diff --git a/locales/en-US.yml b/locales/en-US.yml
index c7512bb941..f354d4c36e 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -462,7 +462,7 @@ securityKeyName: "Key name"
 registerSecurityKey: "Register a security key"
 lastUsed: "Last used"
 unregister: "Unregister"
-passwordLessLogin: "Password-less login"
+passwordLessLogin: "Password-less sign in"
 resetPassword: "Reset password"
 newPasswordIs: "The new password is \"{password}\""
 reduceUiAnimation: "Reduce UI animations"
@@ -527,7 +527,7 @@ disableDrawer: "Don't use drawer-style menus"
 youHaveNoGroups: "You have no groups"
 joinOrCreateGroup: "Get invited to a group or create your own."
 noHistory: "No history available"
-signinHistory: "Login history"
+signinHistory: "Sign in history"
 disableAnimatedMfm: "Disable MFM with animation"
 doing: "Processing..."
 category: "Category"
@@ -717,9 +717,9 @@ useGlobalSetting: "Use global settings"
 useGlobalSettingDesc: "If turned on, your account's notification settings will be
   used. If turned off, individual configurations can be made."
 other: "Other"
-regenerateLoginToken: "Regenerate login token"
-regenerateLoginTokenDescription: "Regenerates the token used internally during login.
-  Normally this action is not necessary. If regenerated, all devices will be logged
+regenerateLoginToken: "Regenerate sign in token"
+regenerateLoginTokenDescription: "Regenerates the token used internally during sign
+  in. Normally this action is not necessary. If regenerated, all devices will be logged
   out."
 setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces."
 fileIdOrUrl: "File ID or URL"
@@ -1001,7 +1001,7 @@ check: "Check"
 driveCapOverrideLabel: "Change the drive capacity for this user"
 driveCapOverrideCaption: "Reset the capacity to default by inputting a value of 0
   or lower."
-requireAdminForView: "You must log in with an administrator account to view this."
+requireAdminForView: "You must sign in with an administrator account to view this."
 isSystemAccount: "This account is created and automatically operated by the system.
   Please do not moderate, edit, delete, or otherwise tamper with this account, or
   it may break your server."
@@ -1011,7 +1011,7 @@ document: "Documentation"
 numberOfPageCache: "Number of cached pages"
 numberOfPageCacheDescription: "Increasing this number will improve convenience for
   users but cause more server load as well as more memory to be used."
-logoutConfirm: "Really log out?"
+logoutConfirm: "Really sign out?"
 lastActiveDate: "Last used at"
 statusbar: "Status bar"
 pleaseSelect: "Select an option"
@@ -1112,6 +1112,9 @@ signupsDisabled: "Signups on this server are currently disabled. If you have an
   code for this server, please enter it below."
 apps: "Apps"
 sendModMail: "Send Moderation Notice"
+moderationNote: "Moderation Note"
+ipFirstAcknowledged: "The date of the first acquisition of the IP address"
+driveCapacityOverride: "Drive Capacity Override"
 preventAiLearning: "Prevent AI bot scraping"
 preventAiLearningDescription: "Request third-party AI language models not to study
   content you upload, such as posts and images."
@@ -1214,7 +1217,7 @@ searchRangeDescription: "If you want to filter the time period, enter it in this
 searchPostsWithFiles: "Only posts with files"
 searchCwAndAlt: "Include content warnings and file descriptions"
 publishTimelines: "Publish timelines for visitors"
-publishTimelinesDescription: "If enabled, the Local and Global timeline will be shown
+publishTimelinesDescription: "If enabled, the Local and Global timelines will be shown
   on {url} even when signed out."
 noAltTextWarning: "Some attached file(s) have no description. Did you forget to write?"
 showNoAltTextWarning: "Show a warning if you attempt to post files without a description"
@@ -1627,7 +1630,7 @@ _2fa:
   step2Url: "You can also enter this URL if you're using a desktop program:"
   step3Title: "Enter an authentication code"
   step3: "Enter the token provided by your app to finish setup."
-  step4: "From now on, any future login attempts will ask for such a login token."
+  step4: "From now on, any future sign in attempts will ask for such a token."
   securityKeyNotSupported: "Your browser does not support security keys."
   securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup
     authentication via hardware security keys that support FIDO2 to further secure
@@ -2220,4 +2223,5 @@ _iconSets:
 moreUrls: "Pinned pages"
 moreUrlsDescription: "Enter the pages you want to pin to the help menu in the lower
   left corner using this notation:\n\"Display name\": https://example.com/"
-messagingUnencryptedInfo: "Chats on Firefish are not end-to-end encrypted. Don't share any sensitive infomation over Firefish."
+messagingUnencryptedInfo: "Chats on Firefish are not end-to-end encrypted. Don't share
+  any sensitive infomation over Firefish."
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 429ce14835..ae612fd478 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -666,7 +666,7 @@ useGlobalSettingDesc: "S'il est activé, les paramètres de notification de votr
 other: "Autre"
 regenerateLoginToken: "Régénérer le jeton de connexion"
 regenerateLoginTokenDescription: "Générer un nouveau jeton d'authentification. Cette
-  opération ne devrait pas être nécessaire ; lors de la génération d'un nouveau jeton,
+  opération ne devrait pas être nécessaire ; lors de la génération d'un nouveau jeton,
   tous les appareils seront déconnectés."
 setMultipleBySeparatingWithSpace: "Vous pouvez en définir plusieurs, en les séparant
   par des espaces."
@@ -2038,7 +2038,7 @@ noInstances: Il n'y a aucun serveur
 showLocalPosts: 'Montrer les notes locales dans :'
 homeTimeline: Timeline d'Accueil
 socialTimeline: Timeline Sociale
-requireAdminForView: Vous avez besoin d'un compte d'administration pour voir cela.
+requireAdminForView: Vous avez besoin d'un compte d'administration pour voir ceci.
 isSystemAccount: Ce compte est créé et géré automatiquement par le système. Veuillez
   ne pas modérer, éditer, supprimer ou altérer d'une autre manière ce compte, ou cela
   risque de perturber votre serveur.
@@ -2091,9 +2091,8 @@ _experiments:
     peut entraîner des ralentissements lors du chargement si votre file d'attente
     est congestionnée.
 userSaysSomethingReasonQuote: '{name} a cité une publication contenant {reason}'
-signupsDisabled: Les inscriptions sur ce serveur sont actuellement désactivés, mais
-  vous pouvez toujours vous inscrire sur un autre serveur ! Si vous avez un code d'invitation
-  pour ce serveur, entrez-le ci-dessous s'il vous plait.
+signupsDisabled: Les inscriptions sur ce serveur sont actuellement désactivés. Si
+  vous avez un code d'invitation pour ce serveur, saisissez-le ci-dessous.
 apps: Applications
 userSaysSomethingReasonReply: '{noms} a répondu à une publication contenant {raison}'
 defaultValueIs: 'défaut : {valeur}'
@@ -2300,3 +2299,16 @@ searchRangeDescription: "Si vous voulez filtrer par période de temps, saisissez
   résultats de recherche pour afficher seulement les messages effectués avant le 2
   janvier de cette année, et 20231026- filtrera les résultats pour afficher seulement
   les messages effectués après le 26 octobre 2023."
+toReply: Répondre
+toPost: Publier
+toQuote: Citer
+toEdit: Modifier
+messagingUnencryptedInfo: Les conversations sur Firefish ne sont pas cryptées. Ne
+  partagez aucune information sensible sur Firefish.
+moderationNote: Note de modération
+driveCapacityOverride: Limite de stockage personalisée
+ipFirstAcknowledged: La date de la première acquisition de l'adresse IP
+noAltTextWarning: Certains fichiers joints n'ont aucune description. Avez-vous oublié
+  de l'écrire ?
+showNoAltTextWarning: Afficher un avertissement si vous essayez de publier des fichiers
+  sans description
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 6b8e2b9407..a3f6a68f2a 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -415,7 +415,7 @@ securityKeyName: "Nama kunci"
 registerSecurityKey: "Daftarkan kunci keamanan"
 lastUsed: "Terakhir digunakan"
 unregister: "Batalkan pendaftaran"
-passwordLessLogin: "Setel login tanpa kata sandi"
+passwordLessLogin: "Masuk tanpa kata sandi"
 resetPassword: "Atur ulang kata sandi"
 newPasswordIs: "Kata sandi baru adalah \"{password}\""
 reduceUiAnimation: "Kurangi animasi antarmuka"
@@ -655,10 +655,10 @@ useGlobalSetting: "Gunakan setelan global"
 useGlobalSettingDesc: "Jika dinyalakan, setelan pemberitahuan akun kamu akan digunakan.
   Jika dimatikan, konfigurasi secara individu dapat dibuat."
 other: "Lainnya"
-regenerateLoginToken: "Perbarui token login"
+regenerateLoginToken: "Perbarui token masuk"
 regenerateLoginTokenDescription: "Perbarui token yang digunakan secara internal saat
-  login. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat akan
-  dilogout."
+  masuk ke akun. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat
+  akan dikeluarkan dari akun."
 setMultipleBySeparatingWithSpace: "Kamu dapat menyetel banyak dengan memisahkannya
   menggunakan spasi."
 fileIdOrUrl: "File-ID atau URL"
@@ -1296,8 +1296,8 @@ _2fa:
   step2Url: "Di aplikasi desktop, masukkan URL berikut:"
   step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan
     pemasangan."
-  step4: "Mulai sekarang, upaya login apapun akan meminta token login dari aplikasi
-    otentikasi kamu."
+  step4: "Mulai sekarang, upaya pemasukan akun apa pun akan meminta token masuk dari
+    aplikasi autentikasi kamu."
   securityKeyInfo: "Kamu dapat memasang otentikasi WebAuthN untuk mengamankan proses
     login lebih lanjut dengan tidak hanya perangkat keras kunci keamanan yang mendukung
     FIDO2, namun juga sidik jari atau otentikasi PIN pada perangkatmu."
@@ -1988,9 +1988,8 @@ moveAccountDescription: Proses ini permanen. Pastikan kamu sudah mengatur alias
   akun ini ke akun barumu sebelum pindah. Silakan masukkan tag akun dengan format
   seperti @orang@server.com
 sendModMail: Kirim Pemberitahuan Moderasi
-signupsDisabled: Pendaftaran ke server ini nonaktif, tapi kamu dapat selalu mendaftar
-  ke server lain! Jika kamu memiliki kode undangan server ini, harap masukkan di bawah
-  ini.
+signupsDisabled: Pendaftaran ke server ini nonaktifkam. Jika kamu memiliki kode undangan
+  server ini, harap masukkan di bawah ini.
 enableCustomKaTeXMacro: Aktifkan makro KaTeX khusus
 isBot: Akun ini akun otomatis
 customMOTD: MOTD khusus (pesan layar percik)
@@ -2185,7 +2184,7 @@ emojiModPerm: Perizinan pengelolaan emoji kustom
 emojiModPermDescription: "Tambah: Perbolehkan pengguna ini untuk menambahkan emoji
   kustom baru dan menetapkan tag/kategori/lisensi untuk semua emoji kustom yang telah
   ditambahkan.\nTambah dan Sunting: Perizinan \"Tambah\" + Perbolehkan pengguna ini
-  untuk menyunting nama/kategori/tag/lisensi emoji kustom yang sudah ada.\nPerbolehkan
+  untuk menyunting nama/kategori/tag/lisensi emoji kustom yang sudah ada.\n Perbolehkan
   Semua: Perizinan \"Tambah dan Sunting\" + Perbolehkan pengguna ini untuk menghapus
   semua emoji kustom yang sudah ada."
 private: Privat
@@ -2241,7 +2240,7 @@ reloading: Memuat ulang
 replyMute: Bisukan balasan dalam lini masa
 searchRange: Dikirim dalam (opsional)
 searchUsersDescription: "Untuk mencari kiriman oleh pengguna/server tertentu, masukkan
-  ID (@pengguna@contoh.id, atau @pengguna untuk pengguna lokal) atau nama domain (contoh.id).\n
+  ID (@pengguna@contoh.id, atau @pengguna untuk pengguna lokal) atau nama domain (contoh.id)\n
   \nJika kamu memasukkan 'me' ('aku', tanpa tanda kutip), semua kirimanmu (termasuk
   kiriman yang tidak terdaftar, khusus pengikut, langsung, dan rahasia) akan dicari.\n
   \nJika Anda memasukkan 'local' (tanpa tanda kutip), hasilnya akan disaring untuk
@@ -2253,3 +2252,16 @@ searchRangeDescription: "Jika kamu ingin memfilter periode waktu, masukkan dalam
   pencarian untuk menampilkan hanya kiriman yang dibuat sebelum tanggal 2 Januari
   tahun ini, dan 20231026- akan memfilter hasil pencarian untuk menampilkan hanya
   kiriman yang dibuat setelah tanggal 26 Oktober 2023."
+toPost: Kirim
+toQuote: Kutip
+noAltTextWarning: Beberapa berkas yang dilampirkan tidak memiliki deskripsi. Lupa
+  menulis deskripsinya?
+toEdit: Sunting
+showNoAltTextWarning: Tampilkan peringatan jika kamu mencoba mengirim berkas tanpa
+  deskripsi
+toReply: Balas
+messagingUnencryptedInfo: Percakapan di Firefish tidak terenkripsi secara ujung ke
+  ujung. Jangan bagikan informasi sensitif apa pun melalui Firefish.
+moderationNote: Catatan Moderasi
+driveCapacityOverride: Penimpaan Kapasitas Drive
+ipFirstAcknowledged: Tanggal akuisisi pertama dari alamat IP
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 6630bdf79b..abbce70fcc 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -107,7 +107,7 @@ cantRenote: "この投稿はブーストできません。"
 cantReRenote: "ブーストをブーストすることはできません。"
 quote: "引用"
 quotes: "引用"
-toQuote: "引用"
+toQuote: "引用する"
 pinnedNote: "ピン留めされた投稿"
 pinned: "ピン留め"
 you: "あなた"
@@ -416,7 +416,7 @@ securityKeyName: "キーの名前"
 registerSecurityKey: "セキュリティキーを登録する"
 lastUsed: "最後の使用"
 unregister: "登録を解除"
-passwordLessLogin: "パスワード無しでログイン"
+passwordLessLogin: "パスワード無しでサインイン"
 resetPassword: "パスワードをリセット"
 newPasswordIs: "新しいパスワードは「{password}」です"
 reduceUiAnimation: "UIのアニメーションを減らす"
@@ -481,7 +481,7 @@ disableDrawer: "メニューをドロワーで表示しない"
 youHaveNoGroups: "グループがありません"
 joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
 noHistory: "履歴はありません"
-signinHistory: "ログイン履歴"
+signinHistory: "サインイン履歴"
 disableAnimatedMfm: "動きのあるMFMを無効にする"
 doing: "やっています"
 category: "カテゴリ"
@@ -648,8 +648,8 @@ notificationSettingDesc: "表示する通知の種別を選択してください
 useGlobalSetting: "グローバル設定を使う"
 useGlobalSettingDesc: "オンにすると、アカウントの通知設定が使用されます。オフにすると、個別に設定できるようになります。"
 other: "その他"
-regenerateLoginToken: "ログイントークンを再生成"
-regenerateLoginTokenDescription: "ログインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスでログアウトされます。"
+regenerateLoginToken: "サインイントークンを再生成"
+regenerateLoginTokenDescription: "サインインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスからサインアウトされます。"
 setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できます。"
 fileIdOrUrl: "ファイルIDまたはURL"
 behavior: "動作"
@@ -908,14 +908,14 @@ thereIsUnresolvedAbuseReportWarning: "未対応の通報があります。"
 check: "チェック"
 driveCapOverrideLabel: "このユーザーのドライブ容量上限を変更"
 driveCapOverrideCaption: "0以下を指定すると解除されます。"
-requireAdminForView: "閲覧するには管理者アカウントでログインしている必要があります。"
+requireAdminForView: "閲覧するには管理者アカウントでサインインしている必要があります。"
 isSystemAccount: "システムにより自動で作成・管理されているアカウントです。モデレーション・編集・削除を行うとサーバーの動作が不正になる可能性があるため、操作しないでください。"
 typeToConfirm: "この操作を行うには {x} と入力してください"
 deleteAccount: "アカウント削除"
 document: "ドキュメント"
 numberOfPageCache: "ページキャッシュ数"
 numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
-logoutConfirm: "ログアウトしますか?"
+logoutConfirm: "サインアウトしますか?"
 lastActiveDate: "最終利用日時"
 statusbar: "ステータスバー"
 pleaseSelect: "選択してください"
@@ -2051,3 +2051,7 @@ makePrivate: "秘密にする"
 makePrivateConfirm: "リモートサーバーに削除リクエストを送信し、投稿の公開範囲を「秘密」にして他の人から見られないようにします。実行しますか?"
 sentFollowRequests: 未承認のフォローリクエスト
 noSentFollowRequests: 未承認のフォローリクエストはありません
+messagingUnencryptedInfo: FirefishのチャットはE2E暗号化されていません。漏洩してはいけない情報はFirefishで送らないでください。
+moderationNote: モデレーション用のメモ
+ipFirstAcknowledged: IPアドレスが最初に取得された日
+driveCapacityOverride: ドライブ容量の変更
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index 51c7b30810..eea5d1b1f6 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -1,7 +1,9 @@
----
 _lang_: "Română"
 headlineFirefish: "O rețea conectată prin note"
-introFirefish: "Bine ai venit! Firefish este un serviciu de microblogging open source și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui. 👍\nHai să explorăm o lume nouă! 🚀"
+introFirefish: "Bine ai venit! Firefish este un serviciu de microblogging open source
+  și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine
+  din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui.
+  👍\nHai să explorăm o lume nouă! 🚀"
 monthAndDay: "{day}/{month}"
 search: "Caută"
 notifications: "Notificări"
@@ -44,7 +46,8 @@ copyContent: "Copiază conținutul"
 copyLink: "Copiază link-ul"
 delete: "Şterge"
 deleteAndEdit: "Șterge și editează"
-deleteAndEditConfirm: "Ești sigur că vrei să ștergi această notă și să o editezi? Vei pierde reacțiile, re-notele și răspunsurile acesteia."
+deleteAndEditConfirm: "Ești sigur că vrei să ștergi această notă și să o editezi?
+  Vei pierde reacțiile, re-notele și răspunsurile acesteia."
 addToList: "Adaugă în listă"
 sendMessage: "Trimite un mesaj"
 copyUsername: "Copiază numele de utilizator"
@@ -64,9 +67,11 @@ import: "Importă"
 export: "Exportă"
 files: "Fișiere"
 download: "Descarcă"
-driveFileDeleteConfirm: "Ești sigur ca vrei să ștergi fișierul \"{name}\"? Notele atașate fișierului vor fi șterse și ele."
+driveFileDeleteConfirm: "Ești sigur ca vrei să ștergi fișierul \"{name}\"? Notele
+  atașate fișierului vor fi șterse și ele."
 unfollowConfirm: "Ești sigur ca vrei să nu mai urmărești pe {name}?"
-exportRequested: "Ai cerut un export. S-ar putea să ia un pic. Va fi adăugat in Drive-ul tău odată completat."
+exportRequested: "Ai cerut un export. S-ar putea să ia un pic. Va fi adăugat in Drive-ul
+  tău odată completat."
 importRequested: "Ai cerut un import. S-ar putea să ia un pic."
 lists: "Liste"
 noLists: "Nu ai nici o listă"
@@ -81,9 +86,12 @@ error: "Eroare"
 somethingHappened: "A survenit o eroare"
 retry: "Reîncearcă"
 pageLoadError: "A apărut o eroare la încărcarea paginii."
-pageLoadErrorDescription: "De obicei asta este cauzat de o eroare de rețea sau cache-ul browser-ului. Încearcă să cureți cache-ul și apoi să încerci din nou puțin mai târziu."
-serverIsDead: "Serverul nu răspunde. Te rugăm să aștepți o perioadă și să încerci din nou."
-youShouldUpgradeClient: "Pentru a vedea această pagină, te rugăm să îți actualizezi clientul."
+pageLoadErrorDescription: "De obicei asta este cauzat de o eroare de rețea sau cache-ul
+  browser-ului. Încearcă să cureți cache-ul și apoi să încerci din nou puțin mai târziu."
+serverIsDead: "Serverul nu răspunde. Te rugăm să aștepți o perioadă și să încerci
+  din nou."
+youShouldUpgradeClient: "Pentru a vedea această pagină, te rugăm să îți actualizezi
+  clientul."
 enterListName: "Introdu un nume pentru listă"
 privacy: "Confidenţialitate"
 makeFollowManuallyApprove: "Fă cererile de urmărire să necesite aprobare"
@@ -137,14 +145,21 @@ emojiUrl: "URL-ul emoji-ului"
 addEmoji: "Adaugă un emoji"
 settingGuide: "Setări recomandate"
 cacheRemoteFiles: "Ține fișierele externe in cache"
-cacheRemoteFilesDescription: "Când această setare este dezactivată, fișierele externe sunt încărcate direct din instanța externă. Dezactivarea va scădea utilizarea spațiului de stocare, dar va crește traficul, deoarece thumbnail-urile nu vor fi generate."
+cacheRemoteFilesDescription: "Când această setare este dezactivată, fișierele externe
+  sunt încărcate direct din instanța externă. Dezactivarea va scădea utilizarea spațiului
+  de stocare, dar va crește traficul, deoarece thumbnail-urile nu vor fi generate."
 flagAsBot: "Marchează acest cont ca bot"
-flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează sistemele interne al Firefish pentru a trata acest cont drept un bot."
+flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de
+  un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori
+  pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează
+  sistemele interne al Firefish pentru a trata acest cont drept un bot."
 flagAsCat: "Marchează acest cont ca pisică"
 flagAsCatDescription: "Activează această opțiune dacă acest cont este o pisică."
 flagShowTimelineReplies: "Arată răspunsurile în cronologie"
-flagShowTimelineRepliesDescription: "Dacă e activată vor fi arătate în cronologie răspunsurile utilizatorilor către alte notele altor utilizatori."
-autoAcceptFollowed: "Aprobă automat cererile de urmărire de la utilizatorii pe care îi urmărești"
+flagShowTimelineRepliesDescription: "Dacă e activată vor fi arătate în cronologie
+  răspunsurile utilizatorilor către alte notele altor utilizatori."
+autoAcceptFollowed: "Aprobă automat cererile de urmărire de la utilizatorii pe care
+  îi urmărești"
 addAccount: "Adaugă un cont"
 loginFailed: "Autentificare eșuată"
 showOnRemote: "Vezi mai multe pe instanța externă"
@@ -156,7 +171,11 @@ searchWith: "Caută: {q}"
 youHaveNoLists: "Nu ai nici o listă"
 followConfirm: "Ești sigur ca vrei să urmărești pe {name}?"
 proxyAccount: "Cont proxy"
-proxyAccountDescription: "Un cont proxy este un cont care se comportă ca un urmăritor extern pentru utilizatorii puși sub anumite condiții. De exemplu, când un cineva adaugă un utilizator extern intr-o listă, activitatea utilizatorului extern nu va fi adusă în instanță daca nici un utilizator local nu urmărește acel utilizator, așa că în schimb contul proxy îl va urmări."
+proxyAccountDescription: "Un cont proxy este un cont care se comportă ca un urmăritor
+  extern pentru utilizatorii puși sub anumite condiții. De exemplu, când un cineva
+  adaugă un utilizator extern intr-o listă, activitatea utilizatorului extern nu va
+  fi adusă în instanță daca nici un utilizator local nu urmărește acel utilizator,
+  așa că în schimb contul proxy îl va urmări."
 host: "Gazdă"
 selectUser: "Selectează un utilizator"
 recipient: "Destinatar"
@@ -186,11 +205,14 @@ instanceInfo: "Informații despre instanță"
 statistics: "Statistici"
 clearQueue: "Șterge coada"
 clearQueueConfirmTitle: "Ești sigur că vrei să cureți coada?"
-clearQueueConfirmText: "Orice notă rămasă în coadă nu va fi federată. De obicei această operație nu este necesară."
+clearQueueConfirmText: "Orice notă rămasă în coadă nu va fi federată. De obicei această
+  operație nu este necesară."
 clearCachedFiles: "Golește cache-ul"
-clearCachedFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele externe din cache?"
+clearCachedFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele externe din
+  cache?"
 blockedInstances: "Instanțe blocate"
-blockedInstancesDescription: "Scrie hostname-urile instanțelor pe care dorești să le blochezi. Instanțele listate nu vor mai putea să comunice cu această instanță."
+blockedInstancesDescription: "Scrie hostname-urile instanțelor pe care dorești să
+  le blochezi. Instanțele listate nu vor mai putea să comunice cu această instanță."
 muteAndBlock: "Amuțiri și Blocări"
 mutedUsers: "Utilizatori amuțiți"
 blockedUsers: "Utilizatori blocați"
@@ -238,7 +260,8 @@ saved: "Salvat"
 messaging: "Chat"
 upload: "Încarcă"
 keepOriginalUploading: "Păstrează imaginea originală"
-keepOriginalUploadingDescription: "Salvează imaginea originala încărcată fără modificări. Dacă e oprită, o versiune pentru afișarea pe web va fi generată la încărcare."
+keepOriginalUploadingDescription: "Salvează imaginea originala încărcată fără modificări.
+  Dacă e oprită, o versiune pentru afișarea pe web va fi generată la încărcare."
 fromDrive: "Din Drive"
 fromUrl: "Din URL"
 uploadFromUrl: "Încarcă dintr-un URL"
@@ -254,7 +277,8 @@ agreeTo: "Sunt de acord cu {0}"
 tos: "Termenii de utilizare"
 start: "Să începem"
 home: "Acasă"
-remoteUserCaution: "Deoarece acest utilizator este dintr-o instanță externă, informația afișată poate fi incompletă."
+remoteUserCaution: "Deoarece acest utilizator este dintr-o instanță externă, informația
+  afișată poate fi incompletă."
 activity: "Activitate"
 images: "Imagini"
 birthday: "Zi de naștere"
@@ -287,7 +311,8 @@ unableToDelete: "Nu se poate șterge"
 inputNewFileName: "Introdu un nou nume de fișier"
 inputNewDescription: "Introdu o descriere nouă"
 inputNewFolderName: "Introdu un nume de folder nou"
-circularReferenceFolder: "Destinația folderului este un subfolder al folderului pe care dorești să îl muți."
+circularReferenceFolder: "Destinația folderului este un subfolder al folderului pe
+  care dorești să îl muți."
 hasChildFilesOrFolders: "Acest folder nu este gol, așa că nu poate fi șters."
 copyUrl: "Copiază URL"
 rename: "Redenumește"
@@ -318,7 +343,8 @@ yearX: "{year}"
 pages: "Pagini"
 enableLocalTimeline: "Activează cronologia locală"
 enableGlobalTimeline: "Activeaza cronologia globală"
-disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate cronologiile, chiar dacă nu sunt activate."
+disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate
+  cronologiile, chiar dacă nu sunt activate."
 registration: "Inregistrare"
 enableRegistration: "Activează înregistrările pentru utilizatori noi"
 invite: "Invită"
@@ -330,9 +356,11 @@ bannerUrl: "URL-ul imaginii de banner"
 backgroundImageUrl: "URL-ul imaginii de fundal"
 basicInfo: "Informații de bază"
 pinnedUsers: "Utilizatori fixați"
-pinnedUsersDescription: "Scrie utilizatorii, separați prin pauză de rând, care vor fi fixați pe pagina \"Explorează\"."
+pinnedUsersDescription: "Scrie utilizatorii, separați prin pauză de rând, care vor
+  fi fixați pe pagina \"Explorează\"."
 pinnedPages: "Pagini fixate"
-pinnedPagesDescription: "Introdu linkurile Paginilor pe care le vrei fixate in vâruful paginii acestei instanțe, separate de pauze de rând."
+pinnedPagesDescription: "Introdu linkurile Paginilor pe care le vrei fixate in vâruful
+  paginii acestei instanțe, separate de pauze de rând."
 pinnedClipId: "ID-ul clip-ului pe care să îl fixezi"
 pinnedNotes: "Notă fixată"
 hcaptcha: "hCaptcha"
@@ -343,14 +371,17 @@ recaptcha: "reCAPTCHA"
 enableRecaptcha: "Activează reCAPTCHA"
 recaptchaSiteKey: "Site key"
 recaptchaSecretKey: "Secret key"
-avoidMultiCaptchaConfirm: "Folosirea mai multor sisteme Captcha poate cauza interferență între acestea. Ai dori să dezactivezi alte sisteme Captcha acum active? Dacă preferi să rămână activate, apasă Anulare."
+avoidMultiCaptchaConfirm: "Folosirea mai multor sisteme Captcha poate cauza interferență
+  între acestea. Ai dori să dezactivezi alte sisteme Captcha acum active? Dacă preferi
+  să rămână activate, apasă Anulare."
 antennas: "Antene"
 manageAntennas: "Gestionează Antenele"
 name: "Nume"
 antennaSource: "Sursa antenei"
 antennaKeywords: "Cuvinte cheie ascultate"
 antennaExcludeKeywords: "Cuvinte cheie excluse"
-antennaKeywordsDescription: "Separă cu spații pentru o condiție ȘI sau cu o întrerupere de rând pentru o condiție SAU."
+antennaKeywordsDescription: "Separă cu spații pentru o condiție ȘI sau cu o întrerupere
+  de rând pentru o condiție SAU."
 notifyAntenna: "Notifică-mă pentru note noi"
 withFileAntenna: "Doar note cu fișiere"
 enableServiceworker: "Activează ServiceWorker"
@@ -437,7 +468,8 @@ strongPassword: "Parolă puternică"
 passwordMatched: "Se potrivește!"
 passwordNotMatched: "Nu se potrivește"
 signinWith: "Autentifică-te cu {x}"
-signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse sunt incorecte."
+signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse
+  sunt incorecte."
 tapSecurityKey: "Apasă pe cheia ta de securitate."
 or: "Sau"
 language: "Limbă"
@@ -478,19 +510,26 @@ showFeaturedNotesInTimeline: "Arată notele recomandate în cronologii"
 objectStorage: "Object Storage"
 useObjectStorage: "Folosește Object Storage"
 objectStorageBaseUrl: "URL de bază"
-objectStorageBaseUrlDesc: "URL-ul este folosit pentru referință. Specifică URL-ul CDN-ului sau Proxy-ului tău dacă folosești unul. Pentru S3 folosește 'https://<bucket>.s3.amazonaws.com' și pentru GCS sau servicii echivalente folosește 'https://storage.googleapis.com/<bucket>', etc."
+objectStorageBaseUrlDesc: "URL-ul este folosit pentru referință. Specifică URL-ul
+  CDN-ului sau Proxy-ului tău dacă folosești unul. Pentru S3 folosește 'https://<bucket>.s3.amazonaws.com'
+  și pentru GCS sau servicii echivalente folosește 'https://storage.googleapis.com/<bucket>',
+  etc."
 objectStorageBucket: "Bucket"
 objectStorageBucketDesc: "Te rog specifică numele bucket-ului furnizorului tău."
 objectStoragePrefix: "Prefix"
 objectStoragePrefixDesc: "Fișierele vor fi stocate sub directoare cu acest prefix."
 objectStorageEndpoint: "Endpoint"
-objectStorageEndpointDesc: "Lasă acest câmp gol dacă folosești AWS S3, dacă nu specifică endpoint-ul ca '<host>' sau '<host>:<port>', depinzând de ce serviciu folosești."
+objectStorageEndpointDesc: "Lasă acest câmp gol dacă folosești AWS S3, dacă nu specifică
+  endpoint-ul ca '<host>' sau '<host>:<port>', depinzând de ce serviciu folosești."
 objectStorageRegion: "Regiune"
-objectStorageRegionDesc: "Specifică o regiune precum 'xx-east-1'. Dacă serviciul tău nu face distincția între regiuni lasă acest câmp gol sau introdu 'us-east-1'."
+objectStorageRegionDesc: "Specifică o regiune precum 'xx-east-1'. Dacă serviciul tău
+  nu face distincția între regiuni lasă acest câmp gol sau introdu 'us-east-1'."
 objectStorageUseSSL: "Folosește SSl"
-objectStorageUseSSLDesc: "Oprește această opțiune dacă nu vei folosi HTTPS pentru conexiunile API-ului"
+objectStorageUseSSLDesc: "Oprește această opțiune dacă nu vei folosi HTTPS pentru
+  conexiunile API-ului"
 objectStorageUseProxy: "Conectează-te prin Proxy"
-objectStorageUseProxyDesc: "Oprește această opțiune dacă vei nu folosi un Proxy pentru conexiunile API-ului"
+objectStorageUseProxyDesc: "Oprește această opțiune dacă vei nu folosi un Proxy pentru
+  conexiunile API-ului"
 objectStorageSetPublicRead: "Setează \"public-read\" pentru încărcare"
 serverLogs: "Loguri server"
 deleteAll: "Șterge tot"
@@ -518,7 +557,9 @@ sort: "Sortează"
 ascendingOrder: "Crescător"
 descendingOrder: "Descrescător"
 scratchpad: "Scratchpad"
-scratchpadDescription: "Scratchpad-ul oferă un mediu de experimentare în AiScript. Poți scrie, executa și verifica rezultatele acestuia interacționând cu Firefish în el."
+scratchpadDescription: "Scratchpad-ul oferă un mediu de experimentare în AiScript.
+  Poți scrie, executa și verifica rezultatele acestuia interacționând cu Firefish
+  în el."
 output: "Ieșire"
 script: "Script"
 disablePagesScript: "Dezactivează AiScript în Pagini"
@@ -526,11 +567,14 @@ updateRemoteUser: "Actualizează informațiile utilizatorului extern"
 deleteAllFiles: "Șterge toate fișierele"
 deleteAllFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele?"
 removeAllFollowing: "Dezurmărește toți utilizatorii urmăriți"
-removeAllFollowingDescription: "Asta va dez-urmări toate conturile din {host}. Te rog execută asta numai dacă instanța, de ex., nu mai există."
+removeAllFollowingDescription: "Asta va dez-urmări toate conturile din {host}. Te
+  rog execută asta numai dacă instanța, de ex., nu mai există."
 userSuspended: "Acest utilizator a fost suspendat."
 userSilenced: "Acest utilizator a fost setat silențios."
 yourAccountSuspendedTitle: "Acest cont a fost suspendat"
-yourAccountSuspendedDescription: "Acest cont a fost suspendat din cauza încălcării termenilor de serviciu al serverului sau ceva similar. Contactează administratorul dacă ai dori să afli un motiv mai detaliat. Te rog nu crea un cont nou."
+yourAccountSuspendedDescription: "Acest cont a fost suspendat din cauza încălcării
+  termenilor de serviciu al serverului sau ceva similar. Contactează administratorul
+  dacă ai dori să afli un motiv mai detaliat. Te rog nu crea un cont nou."
 menu: "Meniu"
 divider: "Separator"
 addItem: "Adaugă element"
@@ -569,12 +613,14 @@ permission: "Permisiuni"
 enableAll: "Actevează tot"
 disableAll: "Dezactivează tot"
 tokenRequested: "Acordă acces la cont"
-pluginTokenRequestedDescription: "Acest plugin va putea să folosească permisiunile setate aici."
+pluginTokenRequestedDescription: "Acest plugin va putea să folosească permisiunile
+  setate aici."
 notificationType: "Tipul notificării"
 edit: "Editează"
 emailServer: "Server email"
 enableEmail: "Activează distribuția de emailuri"
-emailConfigInfo: "Folosit pentru a confirma emailul tău în timpul logări dacă îți uiți parola"
+emailConfigInfo: "Folosit pentru a confirma emailul tău în timpul logări dacă îți
+  uiți parola"
 email: "Email"
 emailAddress: "Adresă de email"
 smtpConfig: "Configurare Server SMTP"
@@ -582,13 +628,15 @@ smtpHost: "Gazdă"
 smtpPort: "Port"
 smtpUser: "Nume de utilizator"
 smtpPass: "Parolă"
-emptyToDisableSmtpAuth: "Lasă username-ul și parola necompletate pentru a dezactiva verificarea SMTP"
+emptyToDisableSmtpAuth: "Lasă username-ul și parola necompletate pentru a dezactiva
+  verificarea SMTP"
 smtpSecure: "Folosește SSL/TLS implicit pentru conecțiunile SMTP"
 smtpSecureInfo: "Oprește opțiunea asta dacă STARTTLS este folosit"
 testEmail: "Testează livrarea emailurilor"
 wordMute: "Cuvinte pe mut"
 regexpError: "Eroare de Expresie Regulată"
-regexpErrorDescription: "A apărut o eroare în expresia regulată pe linia {line} al cuvintelor {tab} setate pe mut:"
+regexpErrorDescription: "A apărut o eroare în expresia regulată pe linia {line} al
+  cuvintelor {tab} setate pe mut:"
 instanceMute: "Instanțe pe mut"
 userSaysSomething: "{name} a spus ceva"
 makeActive: "Activează"
@@ -604,10 +652,13 @@ create: "Crează"
 notificationSetting: "Setări notificări"
 notificationSettingDesc: "Selectează tipurile de notificări care să fie arătate"
 useGlobalSetting: "Folosește setările globale"
-useGlobalSettingDesc: "Dacă opțiunea e pornită, notificările contului tău vor fi folosite. Dacă e oprită, configurația va fi individuală."
+useGlobalSettingDesc: "Dacă opțiunea e pornită, notificările contului tău vor fi folosite.
+  Dacă e oprită, configurația va fi individuală."
 other: "Altele"
 regenerateLoginToken: "Regenerează token de login"
-regenerateLoginTokenDescription: "Regenerează token-ul folosit intern în timpul logări. În mod normal asta nu este necesar. Odată regenerat, toate dispozitivele vor fi delogate."
+regenerateLoginTokenDescription: "Regenerează token-ul folosit intern în timpul logări.
+  În mod normal asta nu este necesar. Odată regenerat, toate dispozitivele vor fi
+  delogate."
 setMultipleBySeparatingWithSpace: "Separă mai multe intrări cu spații."
 fileIdOrUrl: "Introdu ID sau URL"
 behavior: "Comportament"
@@ -615,13 +666,15 @@ sample: "exemplu"
 abuseReports: "Rapoarte"
 reportAbuse: "Raportează"
 reportAbuseOf: "Raportează {name}"
-fillAbuseReportDescription: "Te rog scrie detaliile legate de acest raport. Dacă este despre o notă specifică, te rog introdu URL-ul ei."
+fillAbuseReportDescription: "Te rog scrie detaliile legate de acest raport. Dacă este
+  despre o notă specifică, te rog introdu URL-ul ei."
 abuseReported: "Raportul tău a fost trimis. Mulțumim."
 reporter: "Raportorul"
 reporteeOrigin: "Originea raportatului"
 reporterOrigin: "Originea raportorului"
 forwardReport: "Redirecționează raportul către instanța externă"
-forwardReportIsAnonymous: "În locul contului tău, va fi afișat un cont anonim, de sistem, ca raportor către instanța externă."
+forwardReportIsAnonymous: "În locul contului tău, va fi afișat un cont anonim, de
+  sistem, ca raportor către instanța externă."
 send: "Trimite"
 abuseMarkAsResolved: "Marchează raportul ca rezolvat"
 openInNewTab: "Deschide în tab nou"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 7c33eb815f..30677ef021 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -873,7 +873,7 @@ recommended: "推荐"
 check: "检查"
 driveCapOverrideLabel: "修改此用户的网盘容量"
 driveCapOverrideCaption: "输入 0 或以下的值将容量重置为默认值。"
-requireAdminForView: "需要使用管理员账号登录才能查看。"
+requireAdminForView: "您需要使用管理员账号登录才能查看。"
 isSystemAccount: "该账号由系统自动创建。请不要修改、编辑、删除或以其它方式篡改这个账号,否则可能会破坏您的服务器。"
 typeToConfirm: "输入 {x} 以确认操作"
 deleteAccount: "删除账号"
@@ -1263,7 +1263,7 @@ _2fa:
   step2: "然后,扫描屏幕上显示的二维码。"
   step2Url: "如果您使用的是桌面程序,您也可以输入这个URL:"
   step3: "输入您的应用提供的令牌以完成设置。"
-  step4: "从现在开始,任何登录操作都将要求您提供这样一个登录令牌。"
+  step4: "从现在开始,任何登录操作都将要求您提供这样一个令牌。"
   securityKeyInfo: "除了指纹或 PIN 身份验证外,您还可以通过支持 FIDO2 的硬件安全密钥设置身份验证,以进一步保护您的账号。"
   token: 2FA 令牌
   step3Title: 输入验证码
@@ -1934,7 +1934,10 @@ migrationConfirm: "您确实确定要将账号迁移到 {account} 吗?此操
 noteId: 帖子 ID
 moveFrom: 从旧账号迁移至此账号
 defaultReaction: 发出和收到帖子的默认表情符号反应
-sendModMail: 发送审核通知
+sendModMail: 发送管理通知
+moderationNote: "管理笔记"
+ipFirstAcknowledged: "该日期是这个 IP 地址首次被获取到的日期"
+driveCapacityOverride: "网盘容量变更"
 isLocked: 该账号设置了关注请求
 _filters:
   notesBefore: 帖子早于
@@ -2050,3 +2053,5 @@ searchRangeDescription: "如果您要过滤时间段,请按以下格式输入
   或 20231105-0110),它将被解释为当前年份。\n\n您还可以省略开始日期或结束日期。 例如 -0102 将过滤搜索结果以仅显示今年 1 月 2 日之前发布的帖子,而
   20231026- 将过滤结果以仅显示 2023 年 10 月 26 日之后发布的帖子。"
 messagingUnencryptedInfo: "Firefish 上的聊天没有经过端到端加密,请不要在聊天中分享您的敏感信息。"
+noAltTextWarning: 有些附件没有说明。您是否忘记写说明了?
+showNoAltTextWarning: 当您尝试发布没有说明的帖子附件时显示警告
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 78814ec896..b2c07af664 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -2048,3 +2048,7 @@ searchUsersDescription: "如欲搜尋特定使用者的貼文,請以「@user@e
   \n輸入「me」以搜尋自己的所有貼文(包含不在主頁顯示、追隨者、指定使用者、祕密貼文)。\n\n輸入「local」以搜尋本地伺服器的貼文。"
 searchRangeDescription: "如欲搜尋特定期間的貼文,請以「20220615-20231031」的格式輸入日期範圍。\n\n今年的日期可省略年份(例如0105-0106、20231105-0110)。\n\
   \n開始日期和結果日期可擇一省略。舉例來說,「-0102」表示僅搜尋今年1月2日為止的貼文,「20231026-」表示僅搜尋2023年10月26日以後的貼文。"
+noAltTextWarning: 有些附件沒有說明,您是否忘記寫了?
+moderationNote: 管理員備註
+ipFirstAcknowledged: 首次取得此 IP 位址的日期
+driveCapacityOverride: 雲端硬碟容量變更
diff --git a/package.json b/package.json
index 2b398bf815..fd823ed053 100644
--- a/package.json
+++ b/package.json
@@ -43,14 +43,14 @@
 		"gulp-terser": "2.1.0"
 	},
 	"devDependencies": {
-		"@biomejs/biome": "1.5.3",
-		"@biomejs/cli-darwin-arm64": "^1.5.3",
-		"@biomejs/cli-darwin-x64": "^1.5.3",
-		"@biomejs/cli-linux-arm64": "^1.5.3",
-		"@biomejs/cli-linux-x64": "^1.5.3",
-		"@types/node": "20.11.24",
+		"@biomejs/biome": "1.6.1",
+		"@biomejs/cli-darwin-arm64": "^1.6.1",
+		"@biomejs/cli-darwin-x64": "^1.6.1",
+		"@biomejs/cli-linux-arm64": "^1.6.1",
+		"@biomejs/cli-linux-x64": "^1.6.1",
+		"@types/node": "20.11.28",
 		"execa": "8.0.1",
 		"pnpm": "8.15.4",
-		"typescript": "5.3.3"
+		"typescript": "5.4.2"
 	}
 }
diff --git a/packages/backend-rs/Cargo.lock b/packages/backend-rs/Cargo.lock
index 3b2eef0389..24a232ebce 100644
--- a/packages/backend-rs/Cargo.lock
+++ b/packages/backend-rs/Cargo.lock
@@ -128,9 +128,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.80"
+version = "1.0.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
+checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
 
 [[package]]
 name = "arrayvec"
@@ -180,16 +180,6 @@ dependencies = [
  "num-traits",
 ]
 
-[[package]]
-name = "atomic-write-file"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8204db279bf648d64fe845bd8840f78b39c8132ed4d6a4194c3b10d4b4cfb0b"
-dependencies = [
- "nix",
- "rand",
-]
-
 [[package]]
 name = "autocfg"
 version = "1.1.0"
@@ -342,9 +332,9 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.15.3"
+version = "3.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
+checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
 
 [[package]]
 name = "bytecheck"
@@ -388,9 +378,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
 
 [[package]]
 name = "cc"
-version = "1.0.89"
+version = "1.0.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
 
 [[package]]
 name = "cfg-if"
@@ -421,9 +411,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.2"
+version = "4.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651"
+checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -443,11 +433,11 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "4.5.0"
+version = "4.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
+checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f"
 dependencies = [
- "heck",
+ "heck 0.5.0",
  "proc-macro2",
  "quote",
  "syn 2.0.52",
@@ -868,9 +858,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
 
 [[package]]
 name = "h2"
-version = "0.3.24"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
+checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb"
 dependencies = [
  "bytes",
  "fnv",
@@ -922,6 +912,12 @@ dependencies = [
  "unicode-segmentation",
 ]
 
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
 [[package]]
 name = "hermit-abi"
 version = "0.3.9"
@@ -1321,18 +1317,6 @@ dependencies = [
  "libloading",
 ]
 
-[[package]]
-name = "nix"
-version = "0.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
-dependencies = [
- "bitflags 2.4.2",
- "cfg-if",
- "cfg_aliases",
- "libc",
-]
-
 [[package]]
 name = "nom"
 version = "7.1.3"
@@ -1499,7 +1483,7 @@ version = "0.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8"
 dependencies = [
- "heck",
+ "heck 0.4.1",
  "proc-macro-error",
  "proc-macro2",
  "quote",
@@ -1672,9 +1656,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.78"
+version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
 dependencies = [
  "unicode-ident",
 ]
@@ -1799,9 +1783,9 @@ dependencies = [
 
 [[package]]
 name = "reqwest"
-version = "0.11.24"
+version = "0.11.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
+checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2"
 dependencies = [
  "base64",
  "bytes",
@@ -2015,7 +1999,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114"
 dependencies = [
- "heck",
+ "heck 0.4.1",
  "proc-macro-error",
  "proc-macro2",
  "quote",
@@ -2056,7 +2040,7 @@ version = "0.12.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec13bfb4c4aef208f68dbea970dd40d13830c868aa8dcb4e106b956e6bb4f2fa"
 dependencies = [
- "heck",
+ "heck 0.4.1",
  "proc-macro2",
  "quote",
  "sea-bae",
@@ -2283,9 +2267,9 @@ dependencies = [
 
 [[package]]
 name = "sqlx"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf"
+checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa"
 dependencies = [
  "sqlx-core",
  "sqlx-macros",
@@ -2296,9 +2280,9 @@ dependencies = [
 
 [[package]]
 name = "sqlx-core"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd"
+checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6"
 dependencies = [
  "ahash 0.8.11",
  "atoi",
@@ -2308,7 +2292,6 @@ dependencies = [
  "chrono",
  "crc",
  "crossbeam-queue",
- "dotenvy",
  "either",
  "event-listener",
  "futures-channel",
@@ -2344,9 +2327,9 @@ dependencies = [
 
 [[package]]
 name = "sqlx-macros"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5"
+checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2357,14 +2340,13 @@ dependencies = [
 
 [[package]]
 name = "sqlx-macros-core"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841"
+checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8"
 dependencies = [
- "atomic-write-file",
  "dotenvy",
  "either",
- "heck",
+ "heck 0.4.1",
  "hex",
  "once_cell",
  "proc-macro2",
@@ -2384,9 +2366,9 @@ dependencies = [
 
 [[package]]
 name = "sqlx-mysql"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4"
+checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
 dependencies = [
  "atoi",
  "base64",
@@ -2431,9 +2413,9 @@ dependencies = [
 
 [[package]]
 name = "sqlx-postgres"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24"
+checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
 dependencies = [
  "atoi",
  "base64",
@@ -2462,7 +2444,6 @@ dependencies = [
  "rust_decimal",
  "serde",
  "serde_json",
- "sha1",
  "sha2",
  "smallvec",
  "sqlx-core",
@@ -2476,9 +2457,9 @@ dependencies = [
 
 [[package]]
 name = "sqlx-sqlite"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490"
+checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
 dependencies = [
  "atoi",
  "chrono",
@@ -2639,18 +2620,18 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.57"
+version = "1.0.58"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
+checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.57"
+version = "1.0.58"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
+checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2735,9 +2716,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.14"
+version = "0.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -3013,9 +2994,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
 
 [[package]]
 name = "whoami"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e"
+checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
 dependencies = [
  "redox_syscall",
  "wasite",
diff --git a/packages/backend-rs/Cargo.toml b/packages/backend-rs/Cargo.toml
index 3ab3a4b742..63c32fd031 100644
--- a/packages/backend-rs/Cargo.toml
+++ b/packages/backend-rs/Cargo.toml
@@ -12,20 +12,20 @@ napi = ["dep:napi", "dep:napi-derive"]
 crate-type = ["cdylib", "lib"]
 
 [dependencies]
-async-trait = "0.1.75"
+async-trait = "0.1.77"
 cfg-if = "1.0.0"
-chrono = "0.4.31"
+chrono = "0.4.35"
 cuid2 = "0.1.2"
 jsonschema = "0.17.1"
 once_cell = "1.19.0"
 parse-display = "0.8.2"
 rand = "0.8.5"
 schemars = { version = "0.8.16", features = ["chrono"] }
-sea-orm = { version = "0.12.10", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
-serde = { version = "1.0.193", features = ["derive"] }
-serde_json = "1.0.108"
-thiserror = "1.0.52"
-tokio = { version = "1.35.1", features = ["full"] }
+sea-orm = { version = "0.12.14", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
+serde = { version = "1.0.197", features = ["derive"] }
+serde_json = "1.0.114"
+thiserror = "1.0.58"
+tokio = { version = "1.36.0", features = ["full"] }
 
 # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
 napi = { version = "2.16.0", default-features = false, features = ["napi9", "tokio_rt"], optional = true }
@@ -36,7 +36,7 @@ basen = "0.1.0"
 pretty_assertions = "1.4.0"
 
 [build-dependencies]
-napi-build = "2.1.0"
+napi-build = "2.1.2"
 
 [profile.release]
 lto = true
diff --git a/packages/backend/package.json b/packages/backend/package.json
index e9e7422727..e87c051fb6 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -22,23 +22,23 @@
 		"@swc/core-android-arm64": "1.3.11"
 	},
 	"dependencies": {
-		"@bull-board/api": "5.14.2",
-		"@bull-board/koa": "5.14.2",
-		"@bull-board/ui": "5.14.2",
+		"@bull-board/api": "5.15.1",
+		"@bull-board/koa": "5.15.1",
+		"@bull-board/ui": "5.15.1",
 		"@discordapp/twemoji": "^15.0.2",
 		"@koa/cors": "5.0.0",
 		"@koa/multer": "3.0.2",
 		"@koa/router": "12.0.1",
 		"@ladjs/koa-views": "9.0.0",
 		"@peertube/http-signature": "1.7.0",
-		"@redocly/openapi-core": "1.10.3",
+		"@redocly/openapi-core": "1.10.4",
 		"@sinonjs/fake-timers": "11.2.2",
 		"@twemoji/parser": "^15.0.0",
-		"adm-zip": "^0.5.10",
+		"adm-zip": "^0.5.12",
 		"ajv": "8.12.0",
-		"archiver": "7.0.0",
+		"archiver": "7.0.1",
 		"argon2": "^0.40.1",
-		"aws-sdk": "2.1571.0",
+		"aws-sdk": "2.1578.0",
 		"axios": "^1.6.7",
 		"backend-rs": "workspace:*",
 		"bcryptjs": "2.4.3",
@@ -51,7 +51,7 @@
 		"cli-highlight": "2.1.11",
 		"color-convert": "2.0.1",
 		"content-disposition": "0.5.4",
-		"date-fns": "3.3.1",
+		"date-fns": "3.5.0",
 		"decompress": "^4.2.1",
 		"deep-email-validator": "0.1.21",
 		"deepl-node": "1.12.0",
@@ -60,9 +60,9 @@
 		"file-type": "19.0.0",
 		"fluent-ffmpeg": "2.1.2",
 		"form-data": "^4.0.0",
-		"got": "14.2.0",
+		"got": "14.2.1",
 		"gunzip-maybe": "^1.4.2",
-		"happy-dom": "^13.6.2",
+		"happy-dom": "^13.8.6",
 		"hpagent": "1.2.0",
 		"ioredis": "5.3.2",
 		"ip-cidr": "4.0.0",
@@ -71,7 +71,7 @@
 		"json5": "2.2.3",
 		"jsonld": "8.3.2",
 		"jsrsasign": "11.1.0",
-		"koa": "2.15.0",
+		"koa": "2.15.1",
 		"koa-body": "^6.0.1",
 		"koa-bodyparser": "4.4.1",
 		"koa-favicon": "2.1.0",
@@ -88,7 +88,7 @@
 		"multer": "1.4.5-lts.1",
 		"nested-property": "4.0.0",
 		"node-fetch": "3.3.2",
-		"nodemailer": "6.9.11",
+		"nodemailer": "6.9.12",
 		"opencc-js": "^1.0.5",
 		"os-utils": "0.0.14",
 		"otpauth": "^9.2.2",
@@ -100,7 +100,7 @@
 		"punycode": "2.3.1",
 		"pureimage": "0.4.13",
 		"qrcode": "1.5.3",
-		"qs": "6.11.2",
+		"qs": "6.12.0",
 		"random-seed": "0.3.0",
 		"ratelimiter": "3.4.1",
 		"re2": "1.20.10",
@@ -115,7 +115,7 @@
 		"stringz": "2.1.0",
 		"summaly": "2.7.0",
 		"syslog-pro": "1.0.0",
-		"systeminformation": "5.22.0",
+		"systeminformation": "5.22.2",
 		"tar-stream": "^3.1.7",
 		"tesseract.js": "^5.0.5",
 		"tinycolor2": "1.6.0",
@@ -129,7 +129,7 @@
 	},
 	"devDependencies": {
 		"@swc/cli": "0.3.10",
-		"@swc/core": "1.4.4",
+		"@swc/core": "1.4.8",
 		"@types/adm-zip": "^0.5.5",
 		"@types/bcryptjs": "2.4.6",
 		"@types/color-convert": "^2.0.3",
@@ -138,7 +138,7 @@
 		"@types/fluent-ffmpeg": "2.1.24",
 		"@types/js-yaml": "4.0.9",
 		"@types/jsonld": "1.5.13",
-		"@types/jsrsasign": "10.5.12",
+		"@types/jsrsasign": "10.5.13",
 		"@types/koa": "2.15.0",
 		"@types/koa-bodyparser": "4.3.12",
 		"@types/koa-cors": "0.0.6",
@@ -150,7 +150,7 @@
 		"@types/koa__multer": "2.0.7",
 		"@types/koa__router": "12.0.4",
 		"@types/mocha": "10.0.6",
-		"@types/node": "20.11.24",
+		"@types/node": "20.11.28",
 		"@types/node-fetch": "2.6.11",
 		"@types/nodemailer": "6.4.14",
 		"@types/oauth": "0.9.4",
@@ -183,7 +183,7 @@
 		"ts-loader": "9.5.1",
 		"ts-node": "10.9.2",
 		"tsconfig-paths": "4.2.0",
-		"typescript": "5.3.3",
+		"typescript": "5.4.2",
 		"webpack": "^5.90.3",
 		"ws": "8.16.0"
 	}
diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts
index 5a1e0da454..e54a2889e2 100644
--- a/packages/backend/src/boot/master.ts
+++ b/packages/backend/src/boot/master.ts
@@ -71,7 +71,7 @@ function greet() {
 				136,
 				0,
 			)(
-				" If you like Firefish, please consider starring or contributing to the repo. https://firefish.dev/firefish/firefish",
+				" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
 			),
 		);
 
diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts
index 36a9176b32..284a360dd7 100644
--- a/packages/backend/src/index.ts
+++ b/packages/backend/src/index.ts
@@ -6,7 +6,7 @@ import { EventEmitter } from "node:events";
 import { inspect } from "node:util";
 import boot from "./boot/index.js";
 
-Error.stackTraceLimit = Infinity;
+Error.stackTraceLimit = Number.POSITIVE_INFINITY;
 EventEmitter.defaultMaxListeners = 128;
 
 boot().catch((err) => {
diff --git a/packages/backend/src/migration/1705877093218-remove-native-utils-migration.ts b/packages/backend/src/migration/1705877093218-remove-native-utils-migration.ts
index 098fde3f54..df9569baeb 100644
--- a/packages/backend/src/migration/1705877093218-remove-native-utils-migration.ts
+++ b/packages/backend/src/migration/1705877093218-remove-native-utils-migration.ts
@@ -7,7 +7,7 @@ export class RemoveNativeUtilsMigration1705877093218
 		await queryRunner.query(`DROP TABLE IF EXISTS "reversi_game"`);
 		await queryRunner.query(`DROP TABLE IF EXISTS "reversi_matching"`);
 		await queryRunner.query(
-			`CREATE INDEX IF NOT EXISTS "IDX_note_url" ON "note" ("text")`,
+			`CREATE INDEX IF NOT EXISTS "IDX_note_url" ON "note" ("url")`,
 		);
 		await queryRunner.query(`DROP TABLE IF EXISTS "antenna_note"`);
 		await queryRunner.query(
diff --git a/packages/backend/src/misc/convert-host.ts b/packages/backend/src/misc/convert-host.ts
index 856ce3c127..65cc3ca1f5 100644
--- a/packages/backend/src/misc/convert-host.ts
+++ b/packages/backend/src/misc/convert-host.ts
@@ -1,6 +1,10 @@
 import { URL } from "node:url";
 import config from "@/config/index.js";
 import { toASCII } from "punycode";
+import Logger from "@/services/logger.js";
+import { inspect } from "node:util";
+
+const logger = new Logger("convert-host");
 
 export function getFullApAccount(username: string, host: string | null) {
 	return host
@@ -13,6 +17,20 @@ export function isSelfHost(host: string) {
 	return toPuny(config.host) === toPuny(host);
 }
 
+export function isSameOrigin(src: unknown): boolean | null {
+	if (typeof src !== "string") {
+		logger.debug(`unknown origin: ${inspect(src)}`);
+		return null;
+	}
+	try {
+		const u = new URL(src);
+		return u.origin === config.url;
+	} catch (e) {
+		logger.debug(inspect(e));
+		return false;
+	}
+}
+
 export function extractDbHost(uri: string) {
 	const url = new URL(uri);
 	return toPuny(url.hostname);
diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts
index d880ac9741..7c6105d2f2 100644
--- a/packages/backend/src/models/repositories/user.ts
+++ b/packages/backend/src/models/repositories/user.ts
@@ -513,8 +513,8 @@ export const UserRepository = db.getRepository(User).extend({
 						location: profile!.location,
 						birthday: profile!.birthday,
 						fields: profile!.fields,
-						followersCount: followersCount || 0,
-						followingCount: followingCount || 0,
+						followersCount: followersCount ?? null,
+						followingCount: followingCount ?? null,
 						notesCount: user.notesCount,
 						pinnedNoteIds: pins.map((pin) => pin.noteId),
 						pinnedNotes: Notes.packMany(
@@ -528,8 +528,11 @@ export const UserRepository = db.getRepository(User).extend({
 						pinnedPage: profile!.pinnedPageId
 							? Pages.pack(profile!.pinnedPageId, me)
 							: null,
-						publicReactions: profile!.publicReactions,
-						ffVisibility: profile!.ffVisibility,
+						// TODO: federate publicReactions
+						publicReactions:
+							user.host == null ? profile!.publicReactions : false,
+						// TODO: federate ffVisibility
+						ffVisibility: user.host == null ? profile!.ffVisibility : "private",
 						twoFactorEnabled: profile!.twoFactorEnabled,
 						usePasswordLessLogin: profile!.usePasswordLessLogin,
 						securityKeys: UserSecurityKeys.countBy({
diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts
index 19b8fe49e7..3dc54e1fb5 100644
--- a/packages/backend/src/remote/activitypub/models/note.ts
+++ b/packages/backend/src/remote/activitypub/models/note.ts
@@ -1,6 +1,5 @@
 import promiseLimit from "promise-limit";
 import * as mfm from "mfm-js";
-import config from "@/config/index.js";
 import Resolver from "../resolver.js";
 import post from "@/services/note/create.js";
 import { extractMentionedUsers } from "@/services/note/create.js";
@@ -14,7 +13,7 @@ import { extractPollFromQuestion } from "./question.js";
 import vote from "@/services/note/polls/vote.js";
 import { apLogger } from "../logger.js";
 import { DriveFile } from "@/models/entities/drive-file.js";
-import { extractDbHost, toPuny } from "@/misc/convert-host.js";
+import { extractDbHost, isSameOrigin, toPuny } from "@/misc/convert-host.js";
 import {
 	Emojis,
 	Polls,
@@ -234,7 +233,7 @@ export async function createNote(
 				.catch(async (e) => {
 					// トークだったらinReplyToのエラーは無視
 					const uri = getApId(note.inReplyTo);
-					if (uri.startsWith(`${config.url}/`)) {
+					if (isSameOrigin(uri)) {
 						const id = uri.split("/").pop();
 						const talk = await MessagingMessages.findOneBy({ id });
 						if (talk) {
@@ -439,7 +438,7 @@ export async function resolveNote(
 		}
 		//#endregion
 
-		if (uri.startsWith(config.url)) {
+		if (isSameOrigin(uri)) {
 			throw new StatusError(
 				"cannot resolve local note",
 				400,
@@ -556,7 +555,7 @@ export async function updateNote(value: string | IObject, resolver?: Resolver) {
 	if (!uri) throw new Error("Missing note uri");
 
 	// Skip if URI points to this server
-	if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local");
+	if (isSameOrigin(uri)) throw new Error("uri points local");
 
 	// A new resolver is created if not specified
 	if (resolver == null) resolver = new Resolver();
diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts
index 1b7bd41bbf..6546f041b8 100644
--- a/packages/backend/src/remote/activitypub/models/person.ts
+++ b/packages/backend/src/remote/activitypub/models/person.ts
@@ -19,7 +19,7 @@ import { UserNotePining } from "@/models/entities/user-note-pining.js";
 import { genId } from "@/misc/gen-id.js";
 import { UserPublickey } from "@/models/entities/user-publickey.js";
 import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
-import { toPuny } from "@/misc/convert-host.js";
+import { isSameOrigin, toPuny } from "@/misc/convert-host.js";
 import { UserProfile } from "@/models/entities/user-profile.js";
 import { toArray } from "@/prelude/array.js";
 import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
@@ -138,7 +138,7 @@ export async function fetchPerson(
 	if (cached) return cached;
 
 	// Fetch from the database if the URI points to this server
-	if (uri.startsWith(`${config.url}/`)) {
+	if (isSameOrigin(uri)) {
 		const id = uri.split("/").pop();
 		const u = await Users.findOneBy({ id });
 		if (u) await uriPersonCache.set(uri, u);
@@ -166,7 +166,7 @@ export async function createPerson(
 ): Promise<User> {
 	if (typeof uri !== "string") throw new Error("uri is not string");
 
-	if (uri.startsWith(config.url)) {
+	if (isSameOrigin(uri)) {
 		throw new StatusError(
 			"cannot resolve local user",
 			400,
@@ -419,7 +419,7 @@ export async function updatePerson(
 	if (typeof uri !== "string") throw new Error("uri is not string");
 
 	// Skip if the URI points to this server
-	if (uri.startsWith(`${config.url}/`)) {
+	if (isSameOrigin(uri)) {
 		return;
 	}
 
diff --git a/packages/backend/src/remote/activitypub/models/question.ts b/packages/backend/src/remote/activitypub/models/question.ts
index 39963fddbf..59818654fd 100644
--- a/packages/backend/src/remote/activitypub/models/question.ts
+++ b/packages/backend/src/remote/activitypub/models/question.ts
@@ -1,10 +1,10 @@
-import config from "@/config/index.js";
 import Resolver from "../resolver.js";
 import type { IObject, IQuestion } from "../type.js";
 import { getApId, isQuestion } from "../type.js";
 import { apLogger } from "../logger.js";
 import { Notes, Polls } from "@/models/index.js";
 import type { IPoll } from "@/models/entities/poll.js";
+import { isSameOrigin } from "@/misc/convert-host.js";
 
 export async function extractPollFromQuestion(
 	source: string | IObject,
@@ -57,7 +57,7 @@ export async function updateQuestion(
 	const uri = typeof value === "string" ? value : getApId(value);
 
 	// Skip if URI points to this server
-	if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local");
+	if (isSameOrigin(uri)) throw new Error("uri points local");
 
 	//#region Already registered with this server?
 	const note = await Notes.findOneBy({ uri });
diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts
index f57983d4b9..68f605ecb2 100644
--- a/packages/backend/src/server/api/endpoints/users/followers.ts
+++ b/packages/backend/src/server/api/endpoints/users/followers.ts
@@ -90,6 +90,11 @@ export default define(meta, paramDef, async (ps, me) => {
 
 	const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
 
+	// TODO: federate ffVisibility
+	if (profile.userHost != null) {
+		throw new ApiError(meta.errors.forbidden);
+	}
+
 	if (profile.ffVisibility === "private") {
 		if (me == null || me.id !== user.id) {
 			throw new ApiError(meta.errors.forbidden);
diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts
index 84eb7cccc5..c6e3e06f1d 100644
--- a/packages/backend/src/server/api/endpoints/users/following.ts
+++ b/packages/backend/src/server/api/endpoints/users/following.ts
@@ -89,6 +89,11 @@ export default define(meta, paramDef, async (ps, me) => {
 
 	const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
 
+	// TODO: federate ffVisibility
+	if (profile.userHost != null) {
+		throw new ApiError(meta.errors.forbidden);
+	}
+
 	if (profile.ffVisibility === "private") {
 		if (me == null || me.id !== user.id) {
 			throw new ApiError(meta.errors.forbidden);
diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts
index 483a78865f..1f9aec25a4 100644
--- a/packages/backend/src/server/api/endpoints/users/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/users/reactions.ts
@@ -49,6 +49,11 @@ export const paramDef = {
 export default define(meta, paramDef, async (ps, me) => {
 	const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId });
 
+	// TODO: federate publicReactions
+	if (profile.userHost != null) {
+		throw new ApiError(meta.errors.reactionsNotPublic);
+	}
+
 	if (!profile.publicReactions && (me == null || me.id !== ps.userId)) {
 		throw new ApiError(meta.errors.reactionsNotPublic);
 	}
diff --git a/packages/backend/src/services/chart/core.ts b/packages/backend/src/services/chart/core.ts
index d11b78c671..75177e70d0 100644
--- a/packages/backend/src/services/chart/core.ts
+++ b/packages/backend/src/services/chart/core.ts
@@ -463,7 +463,7 @@ export default abstract class Chart<T extends Schema> {
 	protected commit(diff: Commit<T>, group: string | null = null): void {
 		for (const [k, v] of Object.entries(diff)) {
 			if (v == null || v === 0 || (Array.isArray(v) && v.length === 0))
-				// rome-ignore lint/performance/noDelete: needs to be deleted not just set to undefined
+				// biome-ignore lint/performance/noDelete: needs to be deleted not just set to undefined
 				delete diff[k];
 		}
 		this.buffer.push({
diff --git a/packages/backend/test/activitypub.ts b/packages/backend/test/activitypub.ts
index 7b6f85f5a1..21d5a3b61b 100644
--- a/packages/backend/test/activitypub.ts
+++ b/packages/backend/test/activitypub.ts
@@ -1,6 +1,6 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
+import * as assert from "node:assert";
 import rndstr from "rndstr";
 import { initDb } from "../src/db/postgre.js";
 import { initTestDb } from "./utils.js";
diff --git a/packages/backend/test/ap-request.ts b/packages/backend/test/ap-request.ts
index 722977fe14..0f1e0c58bb 100644
--- a/packages/backend/test/ap-request.ts
+++ b/packages/backend/test/ap-request.ts
@@ -1,4 +1,4 @@
-import * as assert from "assert";
+import * as assert from "node:assert";
 import httpSignature from "@peertube/http-signature";
 import { genRsaKeyPair } from "../src/misc/gen-key-pair.js";
 import {
diff --git a/packages/backend/test/api-visibility.ts b/packages/backend/test/api-visibility.ts
index 49b1b5a064..67ef6e2894 100644
--- a/packages/backend/test/api-visibility.ts
+++ b/packages/backend/test/api-visibility.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import {
 	async,
 	post,
diff --git a/packages/backend/test/api.ts b/packages/backend/test/api.ts
index 0fc2f424ec..86916b1b87 100644
--- a/packages/backend/test/api.ts
+++ b/packages/backend/test/api.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import {
 	async,
 	post,
diff --git a/packages/backend/test/block.ts b/packages/backend/test/block.ts
index 100a4ab7d5..e7bc8a74df 100644
--- a/packages/backend/test/block.ts
+++ b/packages/backend/test/block.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import {
 	async,
 	post,
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 30f36b4653..4495a4264f 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -1,6 +1,6 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
+import * as assert from "node:assert";
 import { inspect } from "node:util";
 import {
 	signup,
diff --git a/packages/backend/test/extract-mentions.ts b/packages/backend/test/extract-mentions.ts
index f400e1e634..cf25a15f6e 100644
--- a/packages/backend/test/extract-mentions.ts
+++ b/packages/backend/test/extract-mentions.ts
@@ -1,4 +1,4 @@
-import * as assert from "assert";
+import * as assert from "node:assert";
 
 import { parse } from "mfm-js";
 import { extractMentions } from "../src/misc/extract-mentions.js";
diff --git a/packages/backend/test/fetch-resource.ts b/packages/backend/test/fetch-resource.ts
index 00c0d736ef..688c3ffa81 100644
--- a/packages/backend/test/fetch-resource.ts
+++ b/packages/backend/test/fetch-resource.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import * as openapi from "@redocly/openapi-core";
 import {
 	async,
diff --git a/packages/backend/test/ff-visibility.ts b/packages/backend/test/ff-visibility.ts
index efdbe7f0f6..2cd4d3c60e 100644
--- a/packages/backend/test/ff-visibility.ts
+++ b/packages/backend/test/ff-visibility.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import {
 	async,
 	connectStream,
diff --git a/packages/backend/test/get-file-info.ts b/packages/backend/test/get-file-info.ts
index b1092af278..cfe1fd445e 100644
--- a/packages/backend/test/get-file-info.ts
+++ b/packages/backend/test/get-file-info.ts
@@ -1,4 +1,4 @@
-import * as assert from "assert";
+import * as assert from "node:assert";
 import { dirname } from "node:path";
 import { fileURLToPath } from "node:url";
 import { getFileInfo } from "../src/misc/get-file-info.js";
diff --git a/packages/backend/test/mfm.ts b/packages/backend/test/mfm.ts
index 97e8e15e91..215da00dea 100644
--- a/packages/backend/test/mfm.ts
+++ b/packages/backend/test/mfm.ts
@@ -1,4 +1,4 @@
-import * as assert from "assert";
+import * as assert from "node:assert";
 import * as mfm from "mfm-js";
 
 import { fromHtml } from "../src/mfm/from-html.js";
diff --git a/packages/backend/test/mute.ts b/packages/backend/test/mute.ts
index 831c2c1ee4..25556954ab 100644
--- a/packages/backend/test/mute.ts
+++ b/packages/backend/test/mute.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import {
 	async,
 	post,
diff --git a/packages/backend/test/note.ts b/packages/backend/test/note.ts
index b78138b1ed..a889b254b5 100644
--- a/packages/backend/test/note.ts
+++ b/packages/backend/test/note.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import { Note } from "../src/models/entities/note.js";
 import {
 	api,
diff --git a/packages/backend/test/prelude/maybe.ts b/packages/backend/test/prelude/maybe.ts
index df589981c3..b88431450a 100644
--- a/packages/backend/test/prelude/maybe.ts
+++ b/packages/backend/test/prelude/maybe.ts
@@ -1,4 +1,4 @@
-import * as assert from "assert";
+import * as assert from "node:assert";
 import { just, nothing } from "../../src/prelude/maybe.js";
 
 describe("just", () => {
diff --git a/packages/backend/test/prelude/url.ts b/packages/backend/test/prelude/url.ts
index 5d08ff8924..684776f52a 100644
--- a/packages/backend/test/prelude/url.ts
+++ b/packages/backend/test/prelude/url.ts
@@ -1,4 +1,4 @@
-import * as assert from "assert";
+import * as assert from "node:assert";
 import { query } from "../../src/prelude/url.js";
 
 describe("url", () => {
diff --git a/packages/backend/test/streaming.ts b/packages/backend/test/streaming.ts
index 37171f4184..6a026b6b72 100644
--- a/packages/backend/test/streaming.ts
+++ b/packages/backend/test/streaming.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import { Following } from "../src/models/entities/following.js";
 import {
 	api,
diff --git a/packages/backend/test/thread-mute.ts b/packages/backend/test/thread-mute.ts
index 88483b51c8..08a32eaf4a 100644
--- a/packages/backend/test/thread-mute.ts
+++ b/packages/backend/test/thread-mute.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import {
 	async,
 	connectStream,
diff --git a/packages/backend/test/tsconfig.json b/packages/backend/test/tsconfig.json
index bc7a9968b5..5fafa1005f 100644
--- a/packages/backend/test/tsconfig.json
+++ b/packages/backend/test/tsconfig.json
@@ -26,16 +26,9 @@
 		"paths": {
 			"@/*": ["../src/*"]
 		},
-		"typeRoots": [
-			"../node_modules/@types",
-			"../src/@types"
-		],
-		"lib": [
-			"esnext"
-		]
+		"typeRoots": ["../node_modules/@types", "../src/@types"],
+		"lib": ["esnext"]
 	},
 	"compileOnSave": false,
-	"include": [
-		"./**/*.ts"
-	]
+	"include": ["./**/*.ts"]
 }
diff --git a/packages/backend/test/user-notes.ts b/packages/backend/test/user-notes.ts
index cdf5e7dbbb..9b68561b4f 100644
--- a/packages/backend/test/user-notes.ts
+++ b/packages/backend/test/user-notes.ts
@@ -1,7 +1,7 @@
 process.env.NODE_ENV = "test";
 
-import * as assert from "assert";
-import * as childProcess from "child_process";
+import * as assert from "node:assert";
+import type * as childProcess from "node:child_process";
 import {
 	async,
 	post,
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index dfacd0738a..b67dc037d4 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -1,11 +1,11 @@
-import * as childProcess from "child_process";
 import { SIGKILL } from "constants";
+import * as childProcess from "node:child_process";
 import * as fs from "node:fs";
 import * as http from "node:http";
 import * as path from "node:path";
 import { dirname } from "node:path";
 import { fileURLToPath } from "node:url";
-import type { endpoints, Entities } from "firefish-js";
+import type { Entities, endpoints } from "firefish-js";
 import FormData from "form-data";
 import got from "got";
 import fetch from "node-fetch";
diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json
index a900763cc4..bee0077992 100644
--- a/packages/backend/tsconfig.json
+++ b/packages/backend/tsconfig.json
@@ -25,24 +25,13 @@
 		"rootDir": "./src",
 		"baseUrl": "./",
 		"paths": {
-			"@/*": [
-				"./src/*"
-			]
+			"@/*": ["./src/*"]
 		},
 		"outDir": "./built",
-		"types": [
-			"node"
-		],
-		"typeRoots": [
-			"./node_modules/@types",
-			"./src/@types"
-		],
-		"lib": [
-			"esnext"
-		]
+		"types": ["node"],
+		"typeRoots": ["./node_modules/@types", "./src/@types"],
+		"lib": ["esnext"]
 	},
 	"compileOnSave": false,
-	"include": [
-		"./src/**/*.ts"
-	]
+	"include": ["./src/**/*.ts"]
 }
diff --git a/packages/client/@types/theme.d.ts b/packages/client/@types/theme.d.ts
index 5f1b81603d..4cfefc55a4 100644
--- a/packages/client/@types/theme.d.ts
+++ b/packages/client/@types/theme.d.ts
@@ -1,5 +1,5 @@
 declare module "@/themes/*.json5" {
-	import { Theme } from "@/scripts/theme";
+	import type { Theme } from "@/scripts/theme";
 
 	const theme: Theme;
 
diff --git a/packages/client/package.json b/packages/client/package.json
index f1b76ad13a..00e6c47d27 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -47,7 +47,7 @@
 		"city-timezones": "^1.2.1",
 		"compare-versions": "6.1.0",
 		"cropperjs": "2.0.0-beta.4",
-		"date-fns": "3.3.1",
+		"date-fns": "3.5.0",
 		"emojilib": "^3.0.11",
 		"eslint-config-prettier": "9.1.0",
 		"eslint-plugin-file-progress": "^1.3.0",
@@ -69,9 +69,9 @@
 		"prettier": "3.2.5",
 		"prismjs": "1.29.0",
 		"punycode": "2.3.1",
-		"rollup": "4.12.0",
+		"rollup": "4.13.0",
 		"s-age": "1.1.2",
-		"sass": "1.71.1",
+		"sass": "1.72.0",
 		"seedrandom": "3.0.5",
 		"stringz": "2.1.0",
 		"swiper": "11.0.7",
@@ -81,10 +81,10 @@
 		"throttle-debounce": "5.0.0",
 		"tinycolor2": "1.6.0",
 		"tinyld": "^1.3.4",
-		"typescript": "5.3.3",
+		"typescript": "5.4.2",
 		"unicode-emoji-json": "^0.4.0",
 		"uuid": "9.0.1",
-		"vite": "5.1.5",
+		"vite": "5.1.6",
 		"vite-plugin-compression": "^0.5.1",
 		"vue": "3.4.21",
 		"vue-draggable-plus": "^0.3.5",
diff --git a/packages/client/src/account.ts b/packages/client/src/account.ts
index 756e94c971..140d865f01 100644
--- a/packages/client/src/account.ts
+++ b/packages/client/src/account.ts
@@ -2,9 +2,8 @@ import type { entities } from "firefish-js";
 import { defineAsyncComponent } from "vue";
 import { i18n } from "./i18n";
 import { apiUrl } from "@/config";
+import { me } from "@/me";
 import { alert, api, popup, popupMenu, waiting } from "@/os";
-import { $i } from "@/reactiveAccount";
-import icon from "@/scripts/icon";
 import { del, get, set } from "@/scripts/idb-proxy";
 import { reloadChannel, unisonReload } from "@/scripts/unison-reload";
 
@@ -12,11 +11,11 @@ import { reloadChannel, unisonReload } from "@/scripts/unison-reload";
 
 export type Account = entities.MeDetailed;
 
-export async function signout() {
+export async function signOut() {
 	waiting();
 	localStorage.removeItem("account");
 
-	await removeAccount($i.id);
+	await removeAccount(me.id);
 
 	const accounts = await getAccounts();
 
@@ -29,7 +28,7 @@ export async function signout() {
 				await fetch(`${apiUrl}/sw/unregister`, {
 					method: "POST",
 					body: JSON.stringify({
-						i: $i.token,
+						i: me.token,
 						endpoint: push.endpoint,
 					}),
 				});
@@ -48,7 +47,7 @@ export async function signout() {
 
 	document.cookie = "igi=; path=/";
 
-	if (accounts.length > 0) login(accounts[0].token);
+	if (accounts.length > 0) signIn(accounts[0].token);
 	else unisonReload("/");
 }
 
@@ -90,7 +89,7 @@ function fetchAccount(token: string): Promise<Account> {
 				if (res.error) {
 					if (res.error.id === "a8c724b3-6e9c-4b46-b1a8-bc3ed6258370") {
 						showSuspendedDialog();
-						signout();
+						signOut();
 					} else {
 						alert({
 							type: "error",
@@ -117,22 +116,23 @@ function showSuspendedDialog() {
 
 export function updateAccount(accountData) {
 	for (const [key, value] of Object.entries(accountData)) {
-		$i[key] = value;
+		me[key] = value;
 	}
-	localStorage.setItem("account", JSON.stringify($i));
+	localStorage.setItem("account", JSON.stringify(me));
 }
 
-export function refreshAccount() {
-	return fetchAccount($i.token).then(updateAccount);
+export async function refreshAccount() {
+	const accountData = await fetchAccount(me.token);
+	return updateAccount(accountData);
 }
 
-export async function login(token: Account["token"], redirect?: string) {
+export async function signIn(token: Account["token"], redirect?: string) {
 	waiting();
 	if (_DEV_) console.log("logging as token ", token);
-	const me = await fetchAccount(token);
-	localStorage.setItem("account", JSON.stringify(me));
+	const newAccount = await fetchAccount(token);
+	localStorage.setItem("account", JSON.stringify(newAccount));
 	document.cookie = `token=${token}; path=/; max-age=31536000`; // bull dashboardの認証とかで使う
-	await addAccount(me.id, token);
+	await addAccount(newAccount.id, token);
 
 	if (redirect) {
 		// 他のタブは再読み込みするだけ
@@ -190,11 +190,11 @@ export async function openAccountMenu(
 	}
 
 	function switchAccountWithToken(token: string) {
-		login(token);
+		signIn(token);
 	}
 
 	const storedAccounts = await getAccounts().then((accounts) =>
-		accounts.filter((x) => x.id !== $i.id),
+		accounts.filter((x) => x.id !== me.id),
 	);
 	const accountsPromise = api("users/show", {
 		userIds: storedAccounts.map((x) => x.id),
@@ -256,12 +256,12 @@ export async function openAccountMenu(
 								{
 									type: "link",
 									text: i18n.ts.profile,
-									to: `/@${$i.username}`,
-									avatar: $i,
+									to: `/@${me.username}`,
+									avatar: me,
 								},
 								null,
 						  ]),
-					...(opts.includeCurrentAccount ? [createItem($i)] : []),
+					...(opts.includeCurrentAccount ? [createItem(me)] : []),
 					...accountItemPromises,
 					...(isMobile ?? false
 						? [
@@ -269,8 +269,8 @@ export async function openAccountMenu(
 								{
 									type: "link",
 									text: i18n.ts.profile,
-									to: `/@${$i.username}`,
-									avatar: $i,
+									to: `/@${me.username}`,
+									avatar: me,
 								},
 						  ]
 						: [
@@ -304,7 +304,7 @@ export async function openAccountMenu(
 	} else {
 		popupMenu(
 			[
-				...(opts.includeCurrentAccount ? [createItem($i)] : []),
+				...(opts.includeCurrentAccount ? [createItem(me)] : []),
 				...accountItemPromises,
 			],
 			ev.currentTarget ?? ev.target,
diff --git a/packages/client/src/components/MkChatPreview.vue b/packages/client/src/components/MkChatPreview.vue
index 2857b001a3..1b330b7b2d 100644
--- a/packages/client/src/components/MkChatPreview.vue
+++ b/packages/client/src/components/MkChatPreview.vue
@@ -4,7 +4,7 @@
 		:class="{
 			isMe: isMe(message),
 			isRead: message.groupId
-				? message.reads.includes($i?.id)
+				? message.reads.includes(me?.id)
 				: message.isRead,
 		}"
 		:to="
@@ -67,14 +67,14 @@
 <script lang="ts" setup>
 import { acct } from "firefish-js";
 import { i18n } from "@/i18n";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 
 defineProps<{
 	message: Record<string, any>;
 }>();
 
 function isMe(message): boolean {
-	return message.userId === $i?.id;
+	return message.userId === me?.id;
 }
 </script>
 
diff --git a/packages/client/src/components/MkCropperDialog.vue b/packages/client/src/components/MkCropperDialog.vue
index 97418aa65d..1dfbd1e2d5 100644
--- a/packages/client/src/components/MkCropperDialog.vue
+++ b/packages/client/src/components/MkCropperDialog.vue
@@ -42,7 +42,7 @@ import Cropper from "cropperjs";
 import tinycolor from "tinycolor2";
 import XModalWindow from "@/components/MkModalWindow.vue";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { defaultStore } from "@/store";
 import { apiUrl, url } from "@/config";
 import { query } from "@/scripts/url";
@@ -81,7 +81,7 @@ const ok = async () => {
 				method: "POST",
 				body: formData,
 				headers: {
-					authorization: `Bearer ${$i.token}`,
+					authorization: `Bearer ${me.token}`,
 				},
 			})
 				.then((response) => response.json())
diff --git a/packages/client/src/components/MkDonation.vue b/packages/client/src/components/MkDonation.vue
index 095dd7a14b..f213867d65 100644
--- a/packages/client/src/components/MkDonation.vue
+++ b/packages/client/src/components/MkDonation.vue
@@ -10,7 +10,7 @@
 				</div>
 				<div :class="$style.text">
 					{{ i18n.ts._aboutFirefish.pleaseDonateToFirefish }}
-					<p v-if="$instance.donationLink">
+					<p v-if="instance.donationLink">
 						{{
 							i18n.t("_aboutFirefish.pleaseDonateToHost", {
 								host: hostname,
@@ -27,9 +27,9 @@
 						>{{ i18n.ts._aboutFirefish.donate }}</MkButton
 					>
 					<MkButton
-						v-if="$instance.donationLink"
+						v-if="instance.donationLink"
 						gradate
-						@click="openExternal($instance.donationLink)"
+						@click="openExternal(instance.donationLink)"
 						>{{
 							i18n.t("_aboutFirefish.donateHost", {
 								host: hostname,
diff --git a/packages/client/src/components/MkDrive.file.vue b/packages/client/src/components/MkDrive.file.vue
index c1507151ca..53847bfd40 100644
--- a/packages/client/src/components/MkDrive.file.vue
+++ b/packages/client/src/components/MkDrive.file.vue
@@ -9,11 +9,11 @@
 		@dragstart="onDragstart"
 		@dragend="onDragend"
 	>
-		<div v-if="$i?.avatarId == file.id" class="label">
+		<div v-if="me?.avatarId == file.id" class="label">
 			<img src="/client-assets/label.svg" />
 			<p>{{ i18n.ts.avatar }}</p>
 		</div>
-		<div v-if="$i?.bannerId == file.id" class="label">
+		<div v-if="me?.bannerId == file.id" class="label">
 			<img src="/client-assets/label.svg" />
 			<p>{{ i18n.ts.banner }}</p>
 		</div>
@@ -45,7 +45,7 @@ import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue";
 import bytes from "@/filters/bytes";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import icon from "@/scripts/icon";
 
 const props = withDefaults(
diff --git a/packages/client/src/components/MkFollowButton.vue b/packages/client/src/components/MkFollowButton.vue
index e705e8c89c..e71463bbe1 100644
--- a/packages/client/src/components/MkFollowButton.vue
+++ b/packages/client/src/components/MkFollowButton.vue
@@ -8,7 +8,7 @@
 		<i :class="icon('ph-dots-three-outline')"></i>
 	</button>
 	<button
-		v-if="!hideFollowButton && isSignedIn && $i.id != user.id"
+		v-if="!hideFollowButton && isSignedIn && me.id != user.id"
 		v-tooltip="full ? null : `${state} ${user.name || user.username}`"
 		class="kpoogebi _button follow-button"
 		:class="{
@@ -66,7 +66,7 @@ import type { entities } from "firefish-js";
 import * as os from "@/os";
 import { useStream } from "@/stream";
 import { i18n } from "@/i18n";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import { getUserMenu } from "@/scripts/get-user-menu";
 import { useRouter } from "@/router";
 import { vibrate } from "@/scripts/vibrate";
diff --git a/packages/client/src/components/MkInstanceTicker.vue b/packages/client/src/components/MkInstanceTicker.vue
index 14a95c58f5..da46185218 100644
--- a/packages/client/src/components/MkInstanceTicker.vue
+++ b/packages/client/src/components/MkInstanceTicker.vue
@@ -55,6 +55,7 @@ const commonNames = new Map<string, string>([
 	["gnusocial", "GNU social"],
 	["gotosocial", "GoToSocial"],
 	["kbin", "/kbin"],
+	["kmyblue", "kmyblue"],
 	["microblogpub", "microblog.pub"],
 	["nextcloud social", "Nextcloud Social"],
 	["peertube", "PeerTube"],
diff --git a/packages/client/src/components/MkMention.vue b/packages/client/src/components/MkMention.vue
index dd2c2e5643..c2c38d313e 100644
--- a/packages/client/src/components/MkMention.vue
+++ b/packages/client/src/components/MkMention.vue
@@ -36,7 +36,7 @@
 <script lang="ts" setup>
 import { toUnicode } from "punycode";
 import { host as localHost } from "@/config";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import { defaultStore } from "@/store";
 
 const props = defineProps<{
@@ -53,8 +53,8 @@ const url = `/${canonical}`;
 
 const isMe =
 	isSignedIn &&
-	`@${props.username}@${toUnicode(props.host)}` ===
-		`@${$i.username}@${toUnicode(localHost)}`.toLowerCase();
+	`@${props.username}@${toUnicode(props.host)}`.toLowerCase() ===
+		`@${me.username}@${toUnicode(localHost)}`.toLowerCase();
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/components/MkNote.vue b/packages/client/src/components/MkNote.vue
index 672939f9d9..e13050206b 100644
--- a/packages/client/src/components/MkNote.vue
+++ b/packages/client/src/components/MkNote.vue
@@ -127,7 +127,7 @@
 							<Mfm
 								:text="translation.text"
 								:author="appearNote.user"
-								:i="$i"
+								:i="me"
 								:lang="targetLang"
 								:custom-emojis="appearNote.emojis"
 							/>
@@ -296,7 +296,7 @@ import { userPage } from "@/filters/user";
 import * as os from "@/os";
 import { defaultStore, noteViewInterruptors } from "@/store";
 import { reactionPicker } from "@/scripts/reaction-picker";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import { i18n } from "@/i18n";
 import { getNoteMenu } from "@/scripts/get-note-menu";
 import { useNoteCapture } from "@/scripts/use-note-capture";
@@ -355,13 +355,13 @@ const reactButton = ref<HTMLElement>();
 const appearNote = computed(() =>
 	isRenote ? (note.value.renote as entities.Note) : note.value,
 );
-const isMyRenote = isSignedIn && $i.id === note.value.userId;
+const isMyRenote = isSignedIn && me.id === note.value.userId;
 const showContent = ref(false);
 const isDeleted = ref(false);
 const muted = ref(
 	getWordSoftMute(
 		note.value,
-		$i?.id,
+		me?.id,
 		defaultStore.state.mutedWords,
 		defaultStore.state.mutedLangs,
 	),
@@ -632,9 +632,7 @@ function setPostExpanded(val: boolean) {
 const accessibleLabel = computed(() => {
 	let label = `${appearNote.value.user.username}; `;
 	if (appearNote.value.renote) {
-		label += `${i18n.t("renoted")} ${
-			appearNote.value.renote.user.username
-		}; `;
+		label += `${i18n.t("renoted")} ${appearNote.value.renote.user.username}; `;
 		if (appearNote.value.renote.cw) {
 			label += `${i18n.t("cw")}: ${appearNote.value.renote.cw}; `;
 			if (postIsExpanded.value) {
diff --git a/packages/client/src/components/MkNoteDetailed.vue b/packages/client/src/components/MkNoteDetailed.vue
index 0e148e7518..d17e21b2b1 100644
--- a/packages/client/src/components/MkNoteDetailed.vue
+++ b/packages/client/src/components/MkNoteDetailed.vue
@@ -180,7 +180,7 @@ import { userPage } from "@/filters/user";
 import * as os from "@/os";
 import { defaultStore, noteViewInterruptors } from "@/store";
 import { reactionPicker } from "@/scripts/reaction-picker";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import { getNoteMenu } from "@/scripts/get-note-menu";
 import { useNoteCapture } from "@/scripts/use-note-capture";
@@ -235,7 +235,7 @@ const isDeleted = ref(false);
 const muted = ref(
 	getWordSoftMute(
 		note.value,
-		$i?.id,
+		me?.id,
 		defaultStore.state.mutedWords,
 		defaultStore.state.mutedLangs,
 	),
diff --git a/packages/client/src/components/MkNotePreview.vue b/packages/client/src/components/MkNotePreview.vue
index 9782778e19..de24d46a90 100644
--- a/packages/client/src/components/MkNotePreview.vue
+++ b/packages/client/src/components/MkNotePreview.vue
@@ -1,17 +1,17 @@
 <template>
 	<div v-size="{ min: [350, 500] }" class="fefdfafb">
-		<MkAvatar class="avatar" :user="$i" disable-link />
+		<MkAvatar class="avatar" :user="me" disable-link />
 		<div class="main">
 			<div class="header">
-				<MkUserName :user="$i" />
+				<MkUserName :user="me" />
 			</div>
 			<div class="body">
 				<div class="content">
 					<Mfm
 						:text="preprocess(text).trim()"
 						:lang="lang"
-						:author="$i"
-						:i="$i"
+						:author="me"
+						:i="me"
 						advanced-mfm
 					/>
 				</div>
diff --git a/packages/client/src/components/MkNoteSub.vue b/packages/client/src/components/MkNoteSub.vue
index 2e5b594a62..b8007ec945 100644
--- a/packages/client/src/components/MkNoteSub.vue
+++ b/packages/client/src/components/MkNoteSub.vue
@@ -50,7 +50,7 @@
 							<Mfm
 								:text="translation.text"
 								:author="appearNote.user"
-								:i="$i"
+								:i="me"
 								:lang="targetLang"
 								:custom-emojis="appearNote.emojis"
 							/>
@@ -211,7 +211,7 @@ import { useRouter } from "@/router";
 import { userPage } from "@/filters/user";
 import * as os from "@/os";
 import { reactionPicker } from "@/scripts/reaction-picker";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import { i18n } from "@/i18n";
 import { useNoteCapture } from "@/scripts/use-note-capture";
 import { defaultStore } from "@/store";
@@ -269,7 +269,7 @@ const isDeleted = ref(false);
 const muted = ref(
 	getWordSoftMute(
 		note.value,
-		$i?.id,
+		me?.id,
 		defaultStore.state.mutedWords,
 		defaultStore.state.mutedLangs,
 	),
diff --git a/packages/client/src/components/MkNotifications.vue b/packages/client/src/components/MkNotifications.vue
index 5751056974..5fff078fb1 100644
--- a/packages/client/src/components/MkNotifications.vue
+++ b/packages/client/src/components/MkNotifications.vue
@@ -54,7 +54,7 @@ import XNotification from "@/components/MkNotification.vue";
 import XList from "@/components/MkDateSeparatedList.vue";
 import XNote from "@/components/MkNote.vue";
 import { useStream } from "@/stream";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 
 const props = defineProps<{
@@ -73,7 +73,7 @@ const pagination: Paging = {
 		includeTypes: props.includeTypes ?? undefined,
 		excludeTypes: props.includeTypes
 			? undefined
-			: $i.mutingNotificationTypes,
+			: me.mutingNotificationTypes,
 		unreadOnly: props.unreadOnly,
 	})),
 };
@@ -81,7 +81,7 @@ const pagination: Paging = {
 const onNotification = (notification) => {
 	const isMuted = props.includeTypes
 		? !props.includeTypes.includes(notification.type)
-		: $i.mutingNotificationTypes.includes(notification.type);
+		: me.mutingNotificationTypes.includes(notification.type);
 	if (isMuted || document.visibilityState === "visible") {
 		stream.send("readNotification", {
 			id: notification.id,
diff --git a/packages/client/src/components/MkPostForm.vue b/packages/client/src/components/MkPostForm.vue
index 2ecc25d665..d3f0a1ee7e 100644
--- a/packages/client/src/components/MkPostForm.vue
+++ b/packages/client/src/components/MkPostForm.vue
@@ -20,7 +20,7 @@
 				class="account _button"
 				@click="openAccountMenu"
 			>
-				<MkAvatar :user="postAccount ?? $i" class="avatar" />
+				<MkAvatar :user="postAccount ?? me" class="avatar" />
 			</button>
 			<div class="right">
 				<span
@@ -322,7 +322,7 @@ import MkInfo from "@/components/MkInfo.vue";
 import { i18n } from "@/i18n";
 import { instance } from "@/instance";
 import { getAccounts, openAccountMenu as openAccountMenu_ } from "@/account";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { uploadFile } from "@/scripts/upload";
 import { deepClone } from "@/scripts/clone";
 import XCheatSheet from "@/components/MkCheatSheetDialog.vue";
@@ -517,7 +517,7 @@ if (props.mention) {
 
 if (
 	props.reply &&
-	(props.reply.user.username !== $i.username ||
+	(props.reply.user.username !== me.username ||
 		(props.reply.user.host != null && props.reply.user.host !== host))
 ) {
 	text.value = `@${props.reply.user.username}${
@@ -539,7 +539,7 @@ if (props.reply && props.reply.text != null) {
 				: `@${x.username}@${toASCII(otherHost)}`;
 
 		// exclude me
-		if ($i.username === x.username && (x.host == null || x.host === host))
+		if (me.username === x.username && (x.host == null || x.host === host))
 			continue;
 
 		// remove duplicates
@@ -573,7 +573,7 @@ if (
 		if (props.reply.visibleUserIds) {
 			os.api("users/show", {
 				userIds: props.reply.visibleUserIds.filter(
-					(uid) => uid !== $i.id && uid !== props.reply.userId,
+					(uid) => uid !== me.id && uid !== props.reply.userId,
 				),
 			}).then((users) => {
 				users.forEach(pushVisibleUser);
@@ -582,7 +582,7 @@ if (
 			visibility.value = "private";
 		}
 
-		if (props.reply.userId !== $i.id) {
+		if (props.reply.userId !== me.id) {
 			os.api("users/show", { userId: props.reply.userId }).then(
 				(user) => {
 					pushVisibleUser(user);
@@ -611,7 +611,7 @@ const addRe = (s: string) => {
 if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
 	useCw.value = true;
 	cw.value =
-		props.reply.user.username === $i.username
+		props.reply.user.username === me.username
 			? props.reply.cw
 			: addRe(props.reply.cw);
 }
@@ -1194,9 +1194,9 @@ function openAccountMenu(ev: MouseEvent) {
 		{
 			withExtraOperation: false,
 			includeCurrentAccount: true,
-			active: postAccount.value != null ? postAccount.value.id : $i.id,
+			active: postAccount.value != null ? postAccount.value.id : me.id,
 			onChoose: (account) => {
-				if (account.id === $i.id) {
+				if (account.id === me.id) {
 					postAccount.value = null;
 				} else {
 					postAccount.value = account;
diff --git a/packages/client/src/components/MkPullToRefresh.vue b/packages/client/src/components/MkPullToRefresh.vue
index e695cce4d6..c7b09a91f1 100644
--- a/packages/client/src/components/MkPullToRefresh.vue
+++ b/packages/client/src/components/MkPullToRefresh.vue
@@ -45,7 +45,6 @@ const isRefreshing = ref(false);
 const pullDistance = ref(0);
 
 let disabled = false;
-
 let supportPointerDesktop = false;
 let startScreenY: number | null = null;
 
diff --git a/packages/client/src/components/MkPushNotificationAllowButton.vue b/packages/client/src/components/MkPushNotificationAllowButton.vue
index 3a7e518859..85232bac7e 100644
--- a/packages/client/src/components/MkPushNotificationAllowButton.vue
+++ b/packages/client/src/components/MkPushNotificationAllowButton.vue
@@ -56,7 +56,7 @@
 import { ref } from "vue";
 
 import { getAccounts } from "@/account";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import MkButton from "@/components/MkButton.vue";
 import { instance } from "@/instance";
 import { api, apiWithDialog, promiseDialog } from "@/os";
@@ -149,7 +149,7 @@ async function unsubscribe() {
 
 	if (isSignedIn && accounts.length >= 2) {
 		apiWithDialog("sw/unregister", {
-			i: $i.token,
+			i: me.token,
 			endpoint,
 		});
 	} else {
@@ -197,7 +197,7 @@ if (navigator.serviceWorker == null) {
 			instance.swPublickey &&
 			"PushManager" in window &&
 			isSignedIn &&
-			$i.token
+			me.token
 		) {
 			supported.value = true;
 
diff --git a/packages/client/src/components/MkQuoteButton.vue b/packages/client/src/components/MkQuoteButton.vue
index 078afec00b..f4004b0cdb 100644
--- a/packages/client/src/components/MkQuoteButton.vue
+++ b/packages/client/src/components/MkQuoteButton.vue
@@ -14,7 +14,7 @@ import { computed } from "vue";
 import type { entities } from "firefish-js";
 import { pleaseLogin } from "@/scripts/please-login";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import { defaultStore } from "@/store";
 import icon from "@/scripts/icon";
@@ -26,7 +26,7 @@ const props = defineProps<{
 const canRenote = computed(
 	() =>
 		["public", "home"].includes(props.note.visibility) ||
-		props.note.userId === $i?.id,
+		props.note.userId === me?.id,
 );
 
 function quote(): void {
diff --git a/packages/client/src/components/MkReactionsViewer.reaction.vue b/packages/client/src/components/MkReactionsViewer.reaction.vue
index f8a074ce7c..c403c7003c 100644
--- a/packages/client/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/client/src/components/MkReactionsViewer.reaction.vue
@@ -28,7 +28,7 @@ import XDetails from "@/components/MkReactionsViewer.details.vue";
 import XReactionIcon from "@/components/MkReactionIcon.vue";
 import * as os from "@/os";
 import { useTooltip } from "@/scripts/use-tooltip";
-import { isSignedIn } from "@/reactiveAccount";
+import { isSignedIn } from "@/me";
 
 const props = defineProps<{
 	reaction: string;
diff --git a/packages/client/src/components/MkReactionsViewer.vue b/packages/client/src/components/MkReactionsViewer.vue
index 710116e25d..9977faf15f 100644
--- a/packages/client/src/components/MkReactionsViewer.vue
+++ b/packages/client/src/components/MkReactionsViewer.vue
@@ -19,7 +19,7 @@
 <script lang="ts" setup>
 import { computed, ref } from "vue";
 import type { entities } from "firefish-js";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import XReaction from "@/components/MkReactionsViewer.reaction.vue";
 
 const props = defineProps<{
@@ -30,7 +30,7 @@ const reactionsEl = ref<HTMLElement>();
 
 const initialReactions = new Set(Object.keys(props.note.reactions));
 
-const isMe = computed(() => isSignedIn && $i.id === props.note.userId);
+const isMe = computed(() => isSignedIn && me.id === props.note.userId);
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/components/MkRenoteButton.vue b/packages/client/src/components/MkRenoteButton.vue
index c3210920b6..8efe5bbaed 100644
--- a/packages/client/src/components/MkRenoteButton.vue
+++ b/packages/client/src/components/MkRenoteButton.vue
@@ -27,7 +27,7 @@ import Ripple from "@/components/MkRipple.vue";
 import XDetails from "@/components/MkUsersTooltip.vue";
 import { pleaseLogin } from "@/scripts/please-login";
 import * as os from "@/os";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import { useTooltip } from "@/scripts/use-tooltip";
 import { i18n } from "@/i18n";
 import { defaultStore } from "@/store";
@@ -46,7 +46,7 @@ const buttonRef = ref<HTMLElement>();
 const canRenote = computed(
 	() =>
 		["public", "home"].includes(props.note.visibility) ||
-		props.note.userId === $i.id,
+		props.note.userId === me.id,
 );
 
 useTooltip(buttonRef, async (showing) => {
@@ -77,7 +77,7 @@ const hasRenotedBefore = ref(false);
 if (isSignedIn) {
 	os.api("notes/renotes", {
 		noteId: props.note.id,
-		userId: $i.id,
+		userId: me.id,
 		limit: 1,
 	}).then((res) => {
 		hasRenotedBefore.value = res.length > 0;
diff --git a/packages/client/src/components/MkSample.vue b/packages/client/src/components/MkSample.vue
index a962f340d2..72fe929ccd 100644
--- a/packages/client/src/components/MkSample.vue
+++ b/packages/client/src/components/MkSample.vue
@@ -52,7 +52,7 @@ export default defineComponent({
 			flag: true,
 			radio: "firefish",
 			mfm: `Hello world! This is an @example mention. BTW, you are @${
-				this.$i ? this.$i.username : "guest"
+				this.me ? this.me.username : "guest"
 			}.\nAlso, here is ${config.url} and [example link](${
 				config.url
 			}). for more details, see <https://firefish.dev/firefish/firefish>.\nAs you know #Firefish is open-source software.`,
diff --git a/packages/client/src/components/MkSearchBox.vue b/packages/client/src/components/MkSearchBox.vue
index 5d57ddcd05..2efd298202 100644
--- a/packages/client/src/components/MkSearchBox.vue
+++ b/packages/client/src/components/MkSearchBox.vue
@@ -130,9 +130,7 @@ const searchUsers = ref(
 );
 const searchRange = ref(
 	searchParams.has("since") || searchParams.has("until")
-		? `${searchParams.get("since") ?? ""}-${
-				searchParams.get("until") ?? ""
-			}`
+		? `${searchParams.get("since") ?? ""}-${searchParams.get("until") ?? ""}`
 		: "",
 );
 const searchPostsWithFiles = ref(searchParams.get("withFiles") === "1");
diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index 7fb2e6f16a..53a593fb13 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -142,7 +142,7 @@ import MkInfo from "@/components/MkInfo.vue";
 import { host as configHost } from "@/config";
 import { byteify, hexify } from "@/scripts/2fa";
 import * as os from "@/os";
-import { login } from "@/account";
+import { signIn } from "@/account";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
@@ -195,7 +195,7 @@ function onUsernameChange() {
 
 function onLogin(res) {
 	if (props.autoSet) {
-		return login(res.i);
+		return signIn(res.i);
 	}
 }
 
diff --git a/packages/client/src/components/MkSignup.vue b/packages/client/src/components/MkSignup.vue
index 3b4cd48890..6de17b2d48 100644
--- a/packages/client/src/components/MkSignup.vue
+++ b/packages/client/src/components/MkSignup.vue
@@ -281,7 +281,7 @@ import MkSwitch from "./form/switch.vue";
 import MkCaptcha from "@/components/MkCaptcha.vue";
 import * as config from "@/config";
 import * as os from "@/os";
-import { login } from "@/account";
+import { signIn } from "@/account";
 import { instance } from "@/instance";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
@@ -470,7 +470,7 @@ function onSubmit(): void {
 					emit("signup", res);
 
 					if (props.autoSet) {
-						login(res.i);
+						signIn(res.i);
 					}
 				});
 			}
diff --git a/packages/client/src/components/MkSubNoteContent.vue b/packages/client/src/components/MkSubNoteContent.vue
index 5eb2d9f203..3c1e2a418b 100644
--- a/packages/client/src/components/MkSubNoteContent.vue
+++ b/packages/client/src/components/MkSubNoteContent.vue
@@ -31,7 +31,7 @@
 			:text="note.cw"
 			:author="note.user"
 			:lang="note.lang"
-			:i="$i"
+			:i="me"
 			:custom-emojis="note.emojis"
 		/>
 	</p>
@@ -103,7 +103,7 @@
 					v-if="note.text"
 					:text="note.text"
 					:author="note.user"
-					:i="$i"
+					:i="me"
 					:lang="note.lang"
 					:custom-emojis="note.emojis"
 				/>
diff --git a/packages/client/src/components/MkTimeline.vue b/packages/client/src/components/MkTimeline.vue
index 8c9a247442..216723d30e 100644
--- a/packages/client/src/components/MkTimeline.vue
+++ b/packages/client/src/components/MkTimeline.vue
@@ -50,7 +50,7 @@ import XNotes from "@/components/MkNotes.vue";
 import MkInfo from "@/components/MkInfo.vue";
 import { useStream } from "@/stream";
 import * as sound from "@/scripts/sound";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import { i18n } from "@/i18n";
 import { defaultStore } from "@/store";
 import icon from "@/scripts/icon";
@@ -103,7 +103,7 @@ const prepend = (note) => {
 	emit("note");
 
 	if (props.sound) {
-		sound.play(isSignedIn && note.userId === $i.id ? "noteMy" : "note");
+		sound.play(isSignedIn && note.userId === me.id ? "noteMy" : "note");
 	}
 };
 
diff --git a/packages/client/src/components/MkTutorialDialog.vue b/packages/client/src/components/MkTutorialDialog.vue
index 9155d48e8a..9616c24802 100644
--- a/packages/client/src/components/MkTutorialDialog.vue
+++ b/packages/client/src/components/MkTutorialDialog.vue
@@ -211,7 +211,7 @@ import MkPushNotificationAllowButton from "@/components/MkPushNotificationAllowB
 import FormSwitch from "@/components/form/switch.vue";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
-import { isModerator } from "@/reactiveAccount";
+import { isModerator } from "@/me";
 import { instance } from "@/instance";
 import icon from "@/scripts/icon";
 
diff --git a/packages/client/src/components/MkUserInfo.vue b/packages/client/src/components/MkUserInfo.vue
index 9229993341..eb4940858a 100644
--- a/packages/client/src/components/MkUserInfo.vue
+++ b/packages/client/src/components/MkUserInfo.vue
@@ -8,7 +8,7 @@
 			:class="{ detailed }"
 		>
 			<span
-				v-if="isSignedIn && $i.id !== user.id && user.isFollowed"
+				v-if="isSignedIn && me.id !== user.id && user.isFollowed"
 				class="followed"
 				>{{ i18n.ts.followsYou }}</span
 			>
@@ -34,7 +34,7 @@
 				class="mfm"
 				:text="user.description"
 				:author="user"
-				:i="$i"
+				:i="me"
 				:custom-emojis="user.emojis"
 			/>
 			<span v-else style="opacity: 0.7">{{
@@ -56,7 +56,7 @@
 					<Mfm
 						:text="field.value"
 						:author="user"
-						:i="$i"
+						:i="me"
 						:custom-emojis="user.emojis"
 						:colored="false"
 					/>
@@ -80,7 +80,7 @@
 		<div class="buttons">
 			<slot>
 				<MkFollowButton
-					v-if="isSignedIn && user.id !== $i.id"
+					v-if="isSignedIn && user.id !== me.id"
 					:user="user"
 				/>
 			</slot>
@@ -97,7 +97,7 @@ import XShowMoreButton from "@/components/MkShowMoreButton.vue";
 import MkNumber from "@/components/MkNumber.vue";
 import { userPage } from "@/filters/user";
 import { i18n } from "@/i18n";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 
 const props = defineProps<{
 	user: entities.UserDetailed;
diff --git a/packages/client/src/components/global/MkPageHeader.vue b/packages/client/src/components/global/MkPageHeader.vue
index c68c2695fd..83b8ceaa0e 100644
--- a/packages/client/src/components/global/MkPageHeader.vue
+++ b/packages/client/src/components/global/MkPageHeader.vue
@@ -20,10 +20,10 @@
 					<i :class="icon('ph-caret-left')"></i>
 				</button>
 				<MkAvatar
-					v-if="narrow && props.displayMyAvatar && $i"
+					v-if="narrow && props.displayMyAvatar && me"
 					v-vibrate="5"
 					class="avatar button"
-					:user="$i"
+					:user="me"
 					:disable-preview="true"
 					disable-link
 					@click.stop="openAccountMenu"
@@ -139,7 +139,7 @@ import { popupMenu } from "@/os";
 import { scrollToTop } from "@/scripts/scroll";
 import { injectPageMetadata } from "@/scripts/page-metadata";
 import { openAccountMenu as openAccountMenu_ } from "@/account";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
diff --git a/packages/client/src/components/global/i18n.ts b/packages/client/src/components/global/i18n.ts
index a0113a6549..33db86c718 100644
--- a/packages/client/src/components/global/i18n.ts
+++ b/packages/client/src/components/global/i18n.ts
@@ -27,14 +27,13 @@ export default defineComponent({
 			if (nextBracketOpen === -1) {
 				parsed.push(str);
 				break;
-			} else {
-				if (nextBracketOpen > 0) parsed.push(str.substr(0, nextBracketOpen));
-				parsed.push({
-					arg: str.substring(nextBracketOpen + 1, nextBracketClose),
-				});
 			}
+			if (nextBracketOpen > 0) parsed.push(str.substring(0, nextBracketOpen));
+			parsed.push({
+				arg: str.substring(nextBracketOpen + 1, nextBracketClose),
+			});
 
-			str = str.substr(nextBracketClose + 1);
+			str = str.substring(nextBracketClose + 1);
 		}
 
 		return h(
diff --git a/packages/client/src/components/page/page.post.vue b/packages/client/src/components/page/page.post.vue
index 1910e8431e..f9806fa3b2 100644
--- a/packages/client/src/components/page/page.post.vue
+++ b/packages/client/src/components/page/page.post.vue
@@ -73,7 +73,7 @@ export default defineComponent({
 						method: "POST",
 						body: formData,
 						headers: {
-							authorization: `Bearer ${this.$i.token}`,
+							authorization: `Bearer ${this.me.token}`,
 						},
 					})
 						.then((response) => response.json())
diff --git a/packages/client/src/components/page/page.text.vue b/packages/client/src/components/page/page.text.vue
index ba7f69a7aa..09ee9c9f2e 100644
--- a/packages/client/src/components/page/page.text.vue
+++ b/packages/client/src/components/page/page.text.vue
@@ -1,6 +1,6 @@
 <template>
 	<div class="mrdgzndn">
-		<Mfm :key="text" :text="text" :is-note="false" :i="$i" />
+		<Mfm :key="text" :text="text" :is-note="false" :i="me" />
 		<MkUrlPreview v-for="url in urls" :key="url" :url="url" class="url" />
 	</div>
 </template>
diff --git a/packages/client/src/components/page/page.vue b/packages/client/src/components/page/page.vue
index e43af6add6..0ee9ce6509 100644
--- a/packages/client/src/components/page/page.vue
+++ b/packages/client/src/components/page/page.vue
@@ -21,7 +21,7 @@ import { Parser } from "@syuilo/aiscript";
 import XBlock from "./page.block.vue";
 import { Hpml } from "@/scripts/hpml/evaluator";
 import { url } from "@/config";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { defaultStore } from "@/store";
 
 export default defineComponent({
@@ -37,7 +37,7 @@ export default defineComponent({
 	setup(props, ctx) {
 		const hpml = new Hpml(props.page, {
 			randomSeed: Math.random(),
-			visitor: $i,
+			visitor: me,
 			url,
 			enableAiScript: !defaultStore.state.disablePagesScript,
 		});
diff --git a/packages/client/src/i18n.ts b/packages/client/src/i18n.ts
index b7aa8c7bef..1b3fdc855d 100644
--- a/packages/client/src/i18n.ts
+++ b/packages/client/src/i18n.ts
@@ -1,13 +1,36 @@
 import { markRaw } from "vue";
 import { locale } from "@/config";
-import { I18n } from "@/scripts/i18n";
 
-export const i18n = markRaw(new I18n(locale));
+class I18n<T extends Record<string, any>> {
+	public ts: T;
 
-// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
-declare module "@vue/runtime-core" {
-	interface ComponentCustomProperties {
-		$t: (typeof i18n)["t"];
-		$ts: (typeof i18n)["locale"];
+	constructor(locale: T) {
+		this.ts = locale;
+
+		// #region BIND
+		this.t = this.t.bind(this);
+		// #endregion
+	}
+
+	// string にしているのは、ドット区切りでのパス指定を許可するため
+	// なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
+	public t(key: string, args?: Record<string, string | number>): string {
+		try {
+			let str = key
+				.split(".")
+				.reduce((o, i) => o[i], this.ts) as unknown as string;
+
+			if (args) {
+				for (const [k, v] of Object.entries(args)) {
+					str = str.replace(`{${k}}`, v.toString());
+				}
+			}
+			return str;
+		} catch (err) {
+			console.warn(`missing localization '${key}'`);
+			return key;
+		}
 	}
 }
+
+export const i18n = markRaw(new I18n(locale));
diff --git a/packages/client/src/init.ts b/packages/client/src/init.ts
index c5e003ed18..53d2062e15 100644
--- a/packages/client/src/init.ts
+++ b/packages/client/src/init.ts
@@ -32,14 +32,14 @@ import {
 } from "vue";
 import { set } from "@/scripts/idb-proxy";
 
-import { login, refreshAccount, signout, updateAccount } from "@/account";
+import { refreshAccount, signIn, signOut, updateAccount } from "@/account";
 import components from "@/components";
 import { lang, ui, version } from "@/config";
 import directives from "@/directives";
 import { i18n } from "@/i18n";
 import { fetchInstance, instance } from "@/instance";
+import { isSignedIn, me } from "@/me";
 import { alert, api, confirm, popup, post, toast } from "@/os";
-import { $i, isSignedIn } from "@/reactiveAccount";
 import { compareFirefishVersions } from "@/scripts/compare-versions";
 import { deviceKind } from "@/scripts/device-kind";
 import { getAccountFromId } from "@/scripts/get-account-from-id";
@@ -76,8 +76,6 @@ function checkForSplash() {
 
 		console.info(`vue ${vueVersion}`);
 
-		(window as any).$i = $i;
-
 		window.addEventListener("error", (event) => {
 			console.error(event);
 			/*
@@ -132,10 +130,10 @@ function checkForSplash() {
 	if (loginId) {
 		const target = getUrlWithoutLoginId(location.href);
 
-		if (!$i || $i.id !== loginId) {
+		if (!me || me.id !== loginId) {
 			const account = await getAccountFromId(loginId);
 			if (account) {
-				await login(account.token, target);
+				await signIn(account.token, target);
 			}
 		}
 
@@ -145,7 +143,7 @@ function checkForSplash() {
 	// #endregion
 
 	// #region Fetch user
-	if ($i?.token) {
+	if (me?.token) {
 		if (_DEV_) {
 			console.log("account cache found. refreshing...");
 		}
@@ -166,7 +164,7 @@ function checkForSplash() {
 
 			try {
 				document.body.innerHTML = "<div>Please wait...</div>";
-				await login(i);
+				await signIn(i);
 			} catch (err) {
 				// Render the error screen
 				// TODO: ちゃんとしたコンポーネントをレンダリングする(v10とかのトラブルシューティングゲーム付きのやつみたいな)
@@ -192,7 +190,7 @@ function checkForSplash() {
 	const app = createApp(
 		window.location.search === "?zen"
 			? defineAsyncComponent(() => import("@/ui/zen.vue"))
-			: !$i
+			: !me
 			  ? defineAsyncComponent(() => import("@/ui/visitor.vue"))
 			  : ui === "deck"
 				  ? defineAsyncComponent(() => import("@/ui/deck.vue"))
@@ -203,11 +201,6 @@ function checkForSplash() {
 		app.config.performance = true;
 	}
 
-	app.config.globalProperties = {
-		$i,
-		$instance: instance,
-	};
-
 	widgets(app);
 	directives(app);
 	components(app);
@@ -259,7 +252,7 @@ function checkForSplash() {
 				defaultStore.state.showUpdates
 			) {
 				// ログインしてる場合だけ
-				if ($i) {
+				if (me) {
 					popup(
 						defineAsyncComponent(() => import("@/components/MkUpdated.vue")),
 						{},
@@ -433,7 +426,7 @@ function checkForSplash() {
 		// only add post shortcuts if logged in
 		hotkeys["p|n"] = post;
 
-		if ($i.isDeleted) {
+		if (me.isDeleted) {
 			alert({
 				type: "warning",
 				text: i18n.ts.accountDeletionInProgress,
@@ -442,12 +435,12 @@ function checkForSplash() {
 
 		const lastUsed = localStorage.getItem("lastUsed");
 		if (lastUsed) {
-			const lastUsedDate = parseInt(lastUsed, 10);
+			const lastUsedDate = Number.parseInt(lastUsed, 10);
 			// 二時間以上前なら
 			if (Date.now() - lastUsedDate > 1000 * 60 * 60 * 2) {
 				toast(
 					i18n.t("welcomeBackWithName", {
-						name: $i.name || $i.username,
+						name: me.name || me.username,
 					}),
 				);
 			}
@@ -460,7 +453,7 @@ function checkForSplash() {
 		const neverShowDonationInfo = localStorage.getItem("neverShowDonationInfo");
 		if (
 			neverShowDonationInfo !== "true" &&
-			new Date($i.createdAt).getTime() < Date.now() - 1000 * 60 * 60 * 24 * 3 &&
+			new Date(me.createdAt).getTime() < Date.now() - 1000 * 60 * 60 * 24 * 3 &&
 			!location.pathname.startsWith("/miauth")
 		) {
 			if (
@@ -549,7 +542,7 @@ function checkForSplash() {
 		// トークンが再生成されたとき
 		// このままではMisskeyが利用できないので強制的にサインアウトさせる
 		main.on("myTokenRegenerated", () => {
-			signout();
+			signOut();
 		});
 	}
 
diff --git a/packages/client/src/instance.ts b/packages/client/src/instance.ts
index 319c95b2ea..7a5a7f4def 100644
--- a/packages/client/src/instance.ts
+++ b/packages/client/src/instance.ts
@@ -46,10 +46,3 @@ export const emojiTags = computed(() => {
 	}
 	return Array.from(tags);
 });
-
-// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
-declare module "@vue/runtime-core" {
-	interface ComponentCustomProperties {
-		$instance: typeof instance;
-	}
-}
diff --git a/packages/client/src/reactiveAccount.ts b/packages/client/src/me.ts
similarity index 51%
rename from packages/client/src/reactiveAccount.ts
rename to packages/client/src/me.ts
index 19863478a2..0add04975b 100644
--- a/packages/client/src/reactiveAccount.ts
+++ b/packages/client/src/me.ts
@@ -4,11 +4,11 @@ import type { Account } from "@/account";
 const accountData = localStorage.getItem("account");
 
 // TODO: 外部からはreadonlyに
-export const $i = accountData
+export const me = accountData
 	? reactive(JSON.parse(accountData) as Account)
 	: null;
 
-export const isSignedIn = $i != null;
-export const isModerator = $i != null && ($i.isModerator || $i.isAdmin);
-export const isEmojiMod = isModerator || $i?.emojiModPerm !== "unauthorized";
-export const isAdmin = $i?.isAdmin;
+export const isSignedIn = me != null;
+export const isModerator = me != null && (me.isModerator || me.isAdmin);
+export const isEmojiMod = isModerator || me?.emojiModPerm !== "unauthorized";
+export const isAdmin = me?.isAdmin;
diff --git a/packages/client/src/navbar.ts b/packages/client/src/navbar.ts
index 1203e37a06..d06c8cf18e 100644
--- a/packages/client/src/navbar.ts
+++ b/packages/client/src/navbar.ts
@@ -1,8 +1,8 @@
 import { computed, reactive } from "vue";
 import { ui } from "@/config";
 import { i18n } from "@/i18n";
+import { isSignedIn, me } from "@/me";
 import * as os from "@/os";
-import { $i, isSignedIn } from "@/reactiveAccount";
 import icon from "@/scripts/icon";
 import { search } from "@/scripts/search";
 import { unisonReload } from "@/scripts/unison-reload";
@@ -12,14 +12,14 @@ export const navbarItemDef = reactive({
 		title: "notifications",
 		icon: `${icon("ph-bell")}`,
 		show: computed(() => isSignedIn),
-		indicated: computed(() => $i?.hasUnreadNotification),
+		indicated: computed(() => me?.hasUnreadNotification),
 		to: "/my/notifications",
 	},
 	messaging: {
 		title: "messaging",
 		icon: `${icon("ph-chats-teardrop")}`,
 		show: computed(() => isSignedIn),
-		indicated: computed(() => $i?.hasUnreadMessagingMessage),
+		indicated: computed(() => me?.hasUnreadMessagingMessage),
 		to: "/my/messaging",
 	},
 	drive: {
@@ -31,8 +31,8 @@ export const navbarItemDef = reactive({
 	followRequests: {
 		title: "followRequests",
 		icon: `${icon("ph-hand-waving")}`,
-		show: computed(() => $i?.isLocked || $i?.hasPendingReceivedFollowRequest),
-		indicated: computed(() => $i?.hasPendingReceivedFollowRequest),
+		show: computed(() => me?.isLocked || me?.hasPendingReceivedFollowRequest),
+		indicated: computed(() => me?.hasPendingReceivedFollowRequest),
 		to: "/my/follow-requests",
 	},
 	explore: {
@@ -43,7 +43,7 @@ export const navbarItemDef = reactive({
 	announcements: {
 		title: "announcements",
 		icon: `${icon("ph-megaphone-simple")}`,
-		indicated: computed(() => $i?.hasUnreadAnnouncement),
+		indicated: computed(() => me?.hasUnreadAnnouncement),
 		to: "/announcements",
 	},
 	search: {
diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts
index 7372ad23a0..c9d1f9b25a 100644
--- a/packages/client/src/os.ts
+++ b/packages/client/src/os.ts
@@ -11,7 +11,7 @@ import MkPostFormDialog from "@/components/MkPostFormDialog.vue";
 import MkToast from "@/components/MkToast.vue";
 import MkWaitingDialog from "@/components/MkWaitingDialog.vue";
 import { apiUrl, url } from "@/config";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import type { MenuItem } from "@/types/menu";
 
 export const pendingApiRequestsCount = ref(0);
@@ -32,7 +32,7 @@ export const api = ((
 		pendingApiRequestsCount.value--;
 	};
 
-	const authorizationToken = token ?? $i?.token ?? undefined;
+	const authorizationToken = token ?? me?.token ?? undefined;
 	const authorization = authorizationToken
 		? `Bearer ${authorizationToken}`
 		: undefined;
@@ -77,7 +77,7 @@ export const apiGet = ((
 
 	const query = new URLSearchParams(data);
 
-	const authorizationToken = token ?? $i?.token ?? undefined;
+	const authorizationToken = token ?? me?.token ?? undefined;
 	const authorization = authorizationToken
 		? `Bearer ${authorizationToken}`
 		: undefined;
diff --git a/packages/client/src/pages/about-firefish.vue b/packages/client/src/pages/about-firefish.vue
index 64c33cee3e..4ddd508fbb 100644
--- a/packages/client/src/pages/about-firefish.vue
+++ b/packages/client/src/pages/about-firefish.vue
@@ -35,7 +35,7 @@
 							><MkEmoji
 								class="emoji"
 								:emoji="emoji.emoji"
-								:custom-emojis="$instance.emojis"
+								:custom-emojis="instance.emojis"
 								:is-reaction="false"
 								:normal="true"
 								:no-style="true"
@@ -96,6 +96,7 @@ import { defaultReactions, defaultStore } from "@/store";
 import * as os from "@/os";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
+import { instance } from "@/instance";
 
 let easterEggReady = false;
 const easterEggEmojis = ref([]);
diff --git a/packages/client/src/pages/about.emojis.vue b/packages/client/src/pages/about.emojis.vue
index 4f4ac5db8a..3efcd665b9 100644
--- a/packages/client/src/pages/about.emojis.vue
+++ b/packages/client/src/pages/about.emojis.vue
@@ -54,9 +54,9 @@ import MkInput from "@/components/form/input.vue";
 import MkSelect from "@/components/form/select.vue";
 import MkFolder from "@/components/MkFolder.vue";
 import MkTab from "@/components/MkTab.vue";
-import { emojiCategories, emojiTags } from "@/instance";
+import { emojiCategories, emojiTags, instance } from "@/instance";
 import { i18n } from "@/i18n";
-import icon from "@/scripts/icon";
+import iconify from "@/scripts/icon";
 
 export default defineComponent({
 	components: {
@@ -72,7 +72,7 @@ export default defineComponent({
 		return {
 			q: "",
 			customEmojiCategories: emojiCategories,
-			customEmojis: this.$instance.emojis,
+			customEmojis: instance.emojis,
 			tags: emojiTags,
 			selectedTags: new Set(),
 			searchEmojis: null,
@@ -127,6 +127,10 @@ export default defineComponent({
 				this.selectedTags.add(tag);
 			}
 		},
+
+		icon(name: string): string {
+			return iconify(name);
+		},
 	},
 });
 </script>
diff --git a/packages/client/src/pages/about.vue b/packages/client/src/pages/about.vue
index 815611cdd5..3f4ddb4d7e 100644
--- a/packages/client/src/pages/about.vue
+++ b/packages/client/src/pages/about.vue
@@ -28,7 +28,7 @@
 						<div
 							class="_formBlock fwhjspax"
 							:style="{
-								backgroundImage: `url(${$instance.bannerUrl})`,
+								backgroundImage: `url(${instance.bannerUrl})`,
 							}"
 						>
 							<div class="content">
@@ -41,7 +41,7 @@
 									@click="easterEgg"
 								/>
 								<div class="name">
-									<b>{{ $instance.name || host }}</b>
+									<b>{{ instance.name || host }}</b>
 								</div>
 							</div>
 						</div>
@@ -49,7 +49,7 @@
 						<MkKeyValue class="_formBlock">
 							<template #key>{{ i18n.ts.description }}</template>
 							<template #value
-								><div v-html="$instance.description"></div
+								><div v-html="instance.description"></div
 							></template>
 						</MkKeyValue>
 
@@ -70,7 +70,7 @@
 										i18n.ts.administrator
 									}}</template>
 									<template #value>{{
-										$instance.maintainerName
+										instance.maintainerName
 									}}</template>
 								</MkKeyValue>
 								<MkKeyValue class="_formBlock">
@@ -78,20 +78,20 @@
 										i18n.ts.contact
 									}}</template>
 									<template #value>{{
-										$instance.maintainerEmail
+										instance.maintainerEmail
 									}}</template>
 								</MkKeyValue>
 							</FormSplit>
 							<FormLink
-								v-if="$instance.tosUrl"
-								:to="$instance.tosUrl"
+								v-if="instance.tosUrl"
+								:to="instance.tosUrl"
 								class="_formBlock"
 								external
 								>{{ i18n.ts.tos }}</FormLink
 							>
 							<FormLink
-								v-if="$instance.donationLink"
-								:to="$instance.donationLink"
+								v-if="instance.donationLink"
+								:to="instance.donationLink"
 								external
 							>
 								<template #icon
@@ -99,7 +99,7 @@
 								></template>
 								{{
 									i18n.t("_aboutFirefish.donateHost", {
-										host: $instance.name || host,
+										host: instance.name || host,
 									})
 								}}
 								<template #suffix>Donate</template>
@@ -190,7 +190,7 @@ import number from "@/filters/number";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { deviceKind } from "@/scripts/device-kind";
-import { isModerator } from "@/reactiveAccount";
+import { isModerator } from "@/me";
 import { instance } from "@/instance";
 import { defaultStore } from "@/store";
 import icon from "@/scripts/icon";
diff --git a/packages/client/src/pages/admin-file.vue b/packages/client/src/pages/admin-file.vue
index 91a4386e16..2ac0f66288 100644
--- a/packages/client/src/pages/admin-file.vue
+++ b/packages/client/src/pages/admin-file.vue
@@ -172,7 +172,7 @@ import * as os from "@/os";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { deviceKind } from "@/scripts/device-kind";
-import { isAdmin, isModerator } from "@/reactiveAccount";
+import { isAdmin, isModerator } from "@/me";
 import { defaultStore } from "@/store";
 import icon from "@/scripts/icon";
 import "swiper/scss";
diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue
index 29c43946f3..53cdad840f 100644
--- a/packages/client/src/pages/admin/index.vue
+++ b/packages/client/src/pages/admin/index.vue
@@ -5,7 +5,7 @@
 				<div class="lxpfedzu">
 					<div class="banner">
 						<img
-							:src="$instance.iconUrl || '/favicon.ico'"
+							:src="instance.iconUrl || '/favicon.ico'"
 							alt=""
 							class="icon"
 						/>
@@ -73,7 +73,7 @@ import MkSuperMenu from "@/components/MkSuperMenu.vue";
 import MkInfo from "@/components/MkInfo.vue";
 import { instance } from "@/instance";
 import { version } from "@/config";
-import { $i } from "@/reactiveAccount";
+import { isAdmin, me } from "@/me";
 import * as os from "@/os";
 import { lookupUser } from "@/scripts/lookup-user";
 import { lookupFile } from "@/scripts/lookup-file";
@@ -214,7 +214,7 @@ const menuDef = computed(() => [
 			},
 		],
 	},
-	...($i?.isAdmin
+	...(isAdmin
 		? [
 				{
 					title: i18n.ts.settings,
diff --git a/packages/client/src/pages/announcements.vue b/packages/client/src/pages/announcements.vue
index befa3e72f7..df2a794ba4 100644
--- a/packages/client/src/pages/announcements.vue
+++ b/packages/client/src/pages/announcements.vue
@@ -59,7 +59,7 @@ import * as os from "@/os";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
-import { isSignedIn } from "@/reactiveAccount";
+import { isSignedIn } from "@/me";
 
 const pagination = {
 	endpoint: "announcements" as const,
diff --git a/packages/client/src/pages/auth.vue b/packages/client/src/pages/auth.vue
index 74b2a8cb53..8a600f7d72 100644
--- a/packages/client/src/pages/auth.vue
+++ b/packages/client/src/pages/auth.vue
@@ -51,8 +51,8 @@ import XForm from "./auth.form.vue";
 import MkSignin from "@/components/MkSignin.vue";
 import MkKeyValue from "@/components/MkKeyValue.vue";
 import * as os from "@/os";
-import { login } from "@/account";
-import { isSignedIn } from "@/reactiveAccount";
+import { signIn } from "@/account";
+import { isSignedIn } from "@/me";
 import { i18n } from "@/i18n";
 
 const props = defineProps<{
@@ -132,6 +132,6 @@ const accepted = () => {
 };
 
 const onLogin = (res) => {
-	login(res.i);
+	signIn(res.i);
 };
 </script>
diff --git a/packages/client/src/pages/channel.vue b/packages/client/src/pages/channel.vue
index 06514a8de9..15e8b6e256 100644
--- a/packages/client/src/pages/channel.vue
+++ b/packages/client/src/pages/channel.vue
@@ -71,13 +71,13 @@
 						<Mfm
 							:text="channel.description"
 							:is-note="false"
-							:i="$i"
+							:i="me"
 						/>
 					</div>
 				</div>
 
 				<XPostForm
-					v-if="$i"
+					v-if="me"
 					:channel="channel"
 					class="post-form _panel _gap"
 					fixed
@@ -103,7 +103,7 @@ import XTimeline from "@/components/MkTimeline.vue";
 import XChannelFollowButton from "@/components/MkChannelFollowButton.vue";
 import * as os from "@/os";
 import { useRouter } from "@/router";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -132,7 +132,7 @@ function edit() {
 }
 
 const headerActions = computed(() => [
-	...(channel.value && channel.value?.userId === $i?.id
+	...(channel.value && channel.value?.userId === me?.id
 		? [
 				{
 					icon: `${icon("ph-gear-six")}`,
diff --git a/packages/client/src/pages/clip.vue b/packages/client/src/pages/clip.vue
index df8e3d88ee..155983ff22 100644
--- a/packages/client/src/pages/clip.vue
+++ b/packages/client/src/pages/clip.vue
@@ -8,7 +8,7 @@
 						<Mfm
 							:text="clip.description"
 							:is-note="false"
-							:i="$i"
+							:i="me"
 						/>
 					</div>
 					<div class="user">
@@ -31,7 +31,7 @@
 import { computed, provide, ref, watch } from "vue";
 import type { entities } from "firefish-js";
 import XNotes from "@/components/MkNotes.vue";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import { i18n } from "@/i18n";
 import * as os from "@/os";
 import { definePageMetadata } from "@/scripts/page-metadata";
@@ -51,7 +51,7 @@ const pagination = {
 };
 
 const isOwned: boolean | null = computed<boolean | null>(
-	() => isSignedIn && clip.value && $i.id === clip.value.userId,
+	() => isSignedIn && clip.value && me.id === clip.value.userId,
 );
 
 watch(
diff --git a/packages/client/src/pages/explore.users.vue b/packages/client/src/pages/explore.users.vue
index 48215ac066..ac68bb8d2d 100644
--- a/packages/client/src/pages/explore.users.vue
+++ b/packages/client/src/pages/explore.users.vue
@@ -146,7 +146,7 @@ import MkFolder from "@/components/MkFolder.vue";
 import MkTab from "@/components/MkTab.vue";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
-import { isSignedIn } from "@/reactiveAccount";
+import { isSignedIn } from "@/me";
 import icon from "@/scripts/icon";
 
 const props = defineProps<{
diff --git a/packages/client/src/pages/follow-requests-sent.vue b/packages/client/src/pages/follow-requests-sent.vue
index 0d22c7b92d..8ca769848b 100644
--- a/packages/client/src/pages/follow-requests-sent.vue
+++ b/packages/client/src/pages/follow-requests-sent.vue
@@ -47,7 +47,7 @@
 										:text="req.followee.description"
 										:is-note="false"
 										:author="req.followee"
-										:i="$i"
+										:i="me"
 										:custom-emojis="req.followee.emojis"
 										:plain="true"
 										:nowrap="true"
@@ -70,7 +70,7 @@ import { userPage } from "@/filters/user";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import icon from "@/scripts/icon";
 
 const paginationComponent = ref<InstanceType<typeof MkPagination>>();
diff --git a/packages/client/src/pages/follow-requests.vue b/packages/client/src/pages/follow-requests.vue
index 13a4981819..d5745346f0 100644
--- a/packages/client/src/pages/follow-requests.vue
+++ b/packages/client/src/pages/follow-requests.vue
@@ -14,7 +14,7 @@
 					</div>
 				</template>
 				<template #default="{ items }">
-					<MkInfo v-if="$i?.isLocked === false" warn class="info"
+					<MkInfo v-if="me?.isLocked === false" warn class="info"
 						>{{ i18n.ts.silencedWarning }}
 					</MkInfo>
 					<div class="mk-follow-requests">
@@ -50,7 +50,7 @@
 										:text="req.follower.description"
 										:is-note="false"
 										:author="req.follower"
-										:i="$i"
+										:i="me"
 										:custom-emojis="req.follower.emojis"
 										:plain="true"
 										:nowrap="true"
@@ -89,7 +89,7 @@ import { userPage } from "@/filters/user";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import icon from "@/scripts/icon";
 
 const paginationComponent = ref<InstanceType<typeof MkPagination>>();
diff --git a/packages/client/src/pages/gallery/post.vue b/packages/client/src/pages/gallery/post.vue
index 384ded3f04..6f5e29ed0b 100644
--- a/packages/client/src/pages/gallery/post.vue
+++ b/packages/client/src/pages/gallery/post.vue
@@ -59,7 +59,7 @@
 								<div class="other">
 									<button
 										v-if="
-											isSignedIn && $i.id === post.user.id
+											isSignedIn && me.id === post.user.id
 										"
 										v-tooltip="i18n.ts.toEdit"
 										v-click-anime
@@ -105,7 +105,7 @@
 									<MkAcct :user="post.user" />
 								</div>
 								<MkFollowButton
-									v-if="!$i || $i.id != post.user.id"
+									v-if="!me || me.id != post.user.id"
 									:user="post.user"
 									:inline="true"
 									:transparent="false"
@@ -163,7 +163,7 @@ import { definePageMetadata } from "@/scripts/page-metadata";
 import { shareAvailable } from "@/scripts/share-available";
 import { defaultStore } from "@/store";
 import icon from "@/scripts/icon";
-import { isSignedIn } from "@/reactiveAccount";
+import { isSignedIn } from "@/me";
 
 const router = useRouter();
 
diff --git a/packages/client/src/pages/instance-info.vue b/packages/client/src/pages/instance-info.vue
index 7da48bc465..e56704879c 100644
--- a/packages/client/src/pages/instance-info.vue
+++ b/packages/client/src/pages/instance-info.vue
@@ -275,7 +275,7 @@ import MkKeyValue from "@/components/MkKeyValue.vue";
 import FormSwitch from "@/components/form/switch.vue";
 import * as os from "@/os";
 import number from "@/filters/number";
-import { isAdmin } from "@/reactiveAccount";
+import { isAdmin } from "@/me";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { deviceKind } from "@/scripts/device-kind";
 import { defaultStore } from "@/store";
diff --git a/packages/client/src/pages/messaging/index.vue b/packages/client/src/pages/messaging/index.vue
index ff42a808e5..8bbabc2fcb 100644
--- a/packages/client/src/pages/messaging/index.vue
+++ b/packages/client/src/pages/messaging/index.vue
@@ -104,7 +104,7 @@ import { useStream } from "@/stream";
 import { useRouter } from "@/router";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { deviceKind } from "@/scripts/device-kind";
 import { defaultStore } from "@/store";
 import icon from "@/scripts/icon";
@@ -203,7 +203,7 @@ function onRead(ids): void {
 			if (found.recipientId) {
 				found.isRead = true;
 			} else if (found.groupId) {
-				found.reads.push($i.id);
+				found.reads.push(me.id);
 			}
 		}
 	}
diff --git a/packages/client/src/pages/messaging/messaging-room.message.vue b/packages/client/src/pages/messaging/messaging-room.message.vue
index 74d1007715..817c2dcd55 100644
--- a/packages/client/src/pages/messaging/messaging-room.message.vue
+++ b/packages/client/src/pages/messaging/messaging-room.message.vue
@@ -25,7 +25,7 @@
 						ref="text"
 						class="text"
 						:text="message.text"
-						:i="$i"
+						:i="me"
 					/>
 				</div>
 				<div v-else class="content">
@@ -90,7 +90,7 @@ import XMediaList from "@/components/MkMediaList.vue";
 import { extractUrlFromMfm } from "@/scripts/extract-url-from-mfm";
 import MkUrlPreview from "@/components/MkUrlPreview.vue";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
@@ -99,7 +99,7 @@ const props = defineProps<{
 	isGroup?: boolean;
 }>();
 
-const isMe = computed(() => props.message.userId === $i?.id);
+const isMe = computed(() => props.message.userId === me?.id);
 const urls = computed(() =>
 	props.message.text ? extractUrlFromMfm(mfm.parse(props.message.text)) : [],
 );
diff --git a/packages/client/src/pages/messaging/messaging-room.vue b/packages/client/src/pages/messaging/messaging-room.vue
index 5930dde8d0..e130a714f5 100644
--- a/packages/client/src/pages/messaging/messaging-room.vue
+++ b/packages/client/src/pages/messaging/messaging-room.vue
@@ -122,7 +122,7 @@ import { useStream } from "@/stream";
 import * as sound from "@/scripts/sound";
 import { vibrate } from "@/scripts/vibrate";
 import { i18n } from "@/i18n";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { defaultStore } from "@/store";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -200,7 +200,7 @@ async function fetch() {
 	connection.value.on("read", onRead);
 	connection.value.on("deleted", onDeleted);
 	connection.value.on("typers", (_typers) => {
-		typers.value = _typers.filter((u) => u.id !== $i?.id);
+		typers.value = _typers.filter((u) => u.id !== me?.id);
 	});
 
 	document.addEventListener("visibilitychange", onVisibilitychange);
@@ -258,7 +258,7 @@ function onMessage(message) {
 	const _isBottom = isBottomVisible(rootEl.value, 64);
 
 	pagingComponent.value.prepend(message);
-	if (message.userId !== $i?.id && !document.hidden) {
+	if (message.userId !== me?.id && !document.hidden) {
 		connection.value?.send("read", {
 			id: message.id,
 		});
@@ -269,7 +269,7 @@ function onMessage(message) {
 		nextTick(() => {
 			thisScrollToBottom();
 		});
-	} else if (message.userId !== $i?.id) {
+	} else if (message.userId !== me?.id) {
 		// Notify
 		notifyNewMessage();
 	}
@@ -341,7 +341,7 @@ function notifyNewMessage() {
 function onVisibilitychange() {
 	if (document.hidden) return;
 	for (const message of pagingComponent.value.items) {
-		if (message.userId !== $i?.id && !message.isRead) {
+		if (message.userId !== me?.id && !message.isRead) {
 			connection.value?.send("read", {
 				id: message.id,
 			});
diff --git a/packages/client/src/pages/miauth.vue b/packages/client/src/pages/miauth.vue
index 34dd7825b9..d8fb31ab92 100644
--- a/packages/client/src/pages/miauth.vue
+++ b/packages/client/src/pages/miauth.vue
@@ -1,6 +1,6 @@
 <template>
 	<MkSpacer :content-max="800">
-		<div v-if="$i">
+		<div v-if="me">
 			<div v-if="state == 'waiting'" class="waiting _section">
 				<div class="_content">
 					<MkLoading />
@@ -82,8 +82,8 @@ import { ref } from "vue";
 import MkSignin from "@/components/MkSignin.vue";
 import MkButton from "@/components/MkButton.vue";
 import * as os from "@/os";
-import { login } from "@/account";
-import { $i } from "@/reactiveAccount";
+import { signIn } from "@/account";
+import { me } from "@/me";
 import { appendQuery, query } from "@/scripts/url";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
@@ -136,7 +136,7 @@ function deny(): void {
 }
 
 function onLogin(res): void {
-	login(res.i);
+	signIn(res.i);
 }
 </script>
 
diff --git a/packages/client/src/pages/page-editor/page-editor.vue b/packages/client/src/pages/page-editor/page-editor.vue
index 4d1608455b..64f5995f99 100644
--- a/packages/client/src/pages/page-editor/page-editor.vue
+++ b/packages/client/src/pages/page-editor/page-editor.vue
@@ -179,7 +179,7 @@ import { selectFile } from "@/scripts/select-file";
 import { mainRouter } from "@/router";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import icon from "@/scripts/icon";
 
 const props = defineProps<{
@@ -189,7 +189,7 @@ const props = defineProps<{
 }>();
 
 const tab = ref("settings");
-const author = ref($i);
+const author = ref(me);
 const readonly = ref(false);
 const page = ref(null);
 const pageId = ref(null);
diff --git a/packages/client/src/pages/page.vue b/packages/client/src/pages/page.vue
index 2023595880..2133d56a3d 100644
--- a/packages/client/src/pages/page.vue
+++ b/packages/client/src/pages/page.vue
@@ -52,7 +52,7 @@
 									/></MkA>
 									<template
 										v-if="
-											isSignedIn && $i.id === page.userId
+											isSignedIn && me.id === page.userId
 										"
 									>
 										<MkA
@@ -63,7 +63,7 @@
 											><i :class="icon('ph-pencil')"
 										/></MkA>
 										<button
-											v-if="$i.pinnedPageId === page.id"
+											v-if="me.pinnedPageId === page.id"
 											v-tooltip="i18n.ts.unpin"
 											class="menu _button"
 											@click="pin(false)"
@@ -150,7 +150,7 @@
 									<MkAcct :user="page.user" />
 								</div>
 								<MkFollowButton
-									v-if="!$i || $i.id != page.user.id"
+									v-if="!me || me.id != page.user.id"
 									:user="page.user"
 									:inline="true"
 									:transparent="false"
@@ -161,9 +161,9 @@
 						</div>
 						<!-- <div class="links">
 						<MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ i18n.ts._pages.viewSource }}</MkA>
-						<template v-if="isSignedIn && $i.id === page.userId">
+						<template v-if="isSignedIn && me.id === page.userId">
 							<MkA :to="`/pages/edit/${page.id}`" class="link">{{ i18n.ts._pages.editThisPage }}</MkA>
-							<button v-if="$i.pinnedPageId === page.id" class="link _textButton" @click="pin(false)">{{ i18n.ts.unpin }}</button>
+							<button v-if="me.pinnedPageId === page.id" class="link _textButton" @click="pin(false)">{{ i18n.ts.unpin }}</button>
 							<button v-else class="link _textButton" @click="pin(true)">{{ i18n.ts.pin }}</button>
 						</template>
 					</div> -->
@@ -215,7 +215,7 @@ import { definePageMetadata } from "@/scripts/page-metadata";
 import { shareAvailable } from "@/scripts/share-available";
 import { defaultStore } from "@/store";
 import icon from "@/scripts/icon";
-import { isSignedIn } from "@/reactiveAccount";
+import { isSignedIn } from "@/me";
 
 const props = defineProps<{
 	pageName: string;
diff --git a/packages/client/src/pages/scratchpad.vue b/packages/client/src/pages/scratchpad.vue
index 232a1ab8c6..179ed8cd4a 100644
--- a/packages/client/src/pages/scratchpad.vue
+++ b/packages/client/src/pages/scratchpad.vue
@@ -50,7 +50,7 @@ import MkContainer from "@/components/MkContainer.vue";
 import MkButton from "@/components/MkButton.vue";
 import { createAiScriptEnv } from "@/scripts/aiscript/api";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -74,7 +74,7 @@ async function run() {
 	const aiscript = new Interpreter(
 		createAiScriptEnv({
 			storageKey: "scratchpad",
-			token: $i?.token,
+			token: me?.token,
 		}),
 		{
 			in: (q) => {
diff --git a/packages/client/src/pages/search.vue b/packages/client/src/pages/search.vue
index 5967b4e545..4f06b18b90 100644
--- a/packages/client/src/pages/search.vue
+++ b/packages/client/src/pages/search.vue
@@ -51,7 +51,7 @@ import { definePageMetadata } from "@/scripts/page-metadata";
 import { defaultStore } from "@/store";
 import { deviceKind } from "@/scripts/device-kind";
 import icon from "@/scripts/icon";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import "swiper/scss";
 import "swiper/scss/virtual";
 import { api } from "@/os";
@@ -96,7 +96,7 @@ const usersPagination = {
 };
 
 async function getUserId(user: string): Promise<string> {
-	if (user === "me") return $i!.id;
+	if (user === "me") return me!.id;
 
 	const split = (user.startsWith("@") ? user.slice(1) : user).split("@");
 	const username = split[0];
diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue
index 8b354e05ef..f27a0c117c 100644
--- a/packages/client/src/pages/settings/2fa.vue
+++ b/packages/client/src/pages/settings/2fa.vue
@@ -2,7 +2,7 @@
 	<FormSection :first="first">
 		<template #label>{{ i18n.ts["2fa"] }}</template>
 
-		<div v-if="$i" class="_gaps_s">
+		<div v-if="me" class="_gaps_s">
 			<MkFolder>
 				<template #icon
 					><i
@@ -12,7 +12,7 @@
 				></template>
 				<template #label>{{ i18n.ts.totp }}</template>
 				<template #caption>{{ i18n.ts.totpDescription }}</template>
-				<div v-if="$i.twoFactorEnabled" class="_gaps_s">
+				<div v-if="me.twoFactorEnabled" class="_gaps_s">
 					<div v-text="i18n.ts._2fa.alreadyRegistered" />
 					<MkButton @click="unregisterTOTP"
 						><i
@@ -24,7 +24,7 @@
 				</div>
 
 				<MkButton
-					v-else-if="!twoFactorData && !$i.twoFactorEnabled"
+					v-else-if="!twoFactorData && !me.twoFactorEnabled"
 					@click="registerTOTP"
 					>{{ i18n.ts._2fa.registerTOTP }}</MkButton
 				>
@@ -55,7 +55,7 @@
 							>{{ i18n.ts._2fa.registerSecurityKey }}</MkButton
 						>
 						<MkFolder
-							v-for="key in $i.securityKeysList"
+							v-for="key in me.securityKeysList"
 							:key="key.id"
 						>
 							<h3>{{ key.name }}</h3>
@@ -79,7 +79,7 @@
 
 			<MkSwitch
 				:disabled="
-					!$i.twoFactorEnabled || $i.securityKeysList.length === 0
+					!me.twoFactorEnabled || me.securityKeysList.length === 0
 				"
 				:model-value="usePasswordLessLogin"
 				@update:modelValue="(v) => updatePasswordLessLogin(v)"
@@ -103,7 +103,7 @@ import MkSwitch from "@/components/form/switch.vue";
 import FormSection from "@/components/form/section.vue";
 import MkFolder from "@/components/MkFolder.vue";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
@@ -120,7 +120,7 @@ withDefaults(
 
 const twoFactorData = ref<any>(null);
 const supportsCredentials = ref(!!navigator.credentials);
-const usePasswordLessLogin = computed(() => $i!.usePasswordLessLogin);
+const usePasswordLessLogin = computed(() => me!.usePasswordLessLogin);
 
 async function registerTOTP() {
 	const password = await os.inputText({
@@ -252,9 +252,9 @@ async function addSecurityKey() {
 				name: "Firefish",
 			},
 			user: {
-				id: byteify($i!.id, "ascii"),
-				name: $i!.username,
-				displayName: $i!.name,
+				id: byteify(me!.id, "ascii"),
+				name: me!.username,
+				displayName: me!.name,
 			},
 			pubKeyCredParams: [{ alg: -7, type: "public-key" }],
 			timeout: 60000,
diff --git a/packages/client/src/pages/settings/account-info.vue b/packages/client/src/pages/settings/account-info.vue
index fa89f7601d..da7c95af65 100644
--- a/packages/client/src/pages/settings/account-info.vue
+++ b/packages/client/src/pages/settings/account-info.vue
@@ -3,7 +3,7 @@
 		<MkKeyValue>
 			<template #key>ID</template>
 			<template #value
-				><span class="_monospace">{{ $i.id }}</span></template
+				><span class="_monospace">{{ me.id }}</span></template
 			>
 		</MkKeyValue>
 
@@ -11,7 +11,7 @@
 			<MkKeyValue>
 				<template #key>{{ i18n.ts.registeredDate }}</template>
 				<template #value
-					><MkTime :time="$i.createdAt" mode="detail"
+					><MkTime :time="me.createdAt" mode="detail"
 				/></template>
 			</MkKeyValue>
 		</FormSection>
@@ -135,25 +135,25 @@
 			<MkKeyValue oneline style="margin: 1em 0">
 				<template #key>emailVerified</template>
 				<template #value>{{
-					$i.emailVerified ? i18n.ts.yes : i18n.ts.no
+					me.emailVerified ? i18n.ts.yes : i18n.ts.no
 				}}</template>
 			</MkKeyValue>
 			<MkKeyValue oneline style="margin: 1em 0">
 				<template #key>twoFactorEnabled</template>
 				<template #value>{{
-					$i.twoFactorEnabled ? i18n.ts.yes : i18n.ts.no
+					me.twoFactorEnabled ? i18n.ts.yes : i18n.ts.no
 				}}</template>
 			</MkKeyValue>
 			<MkKeyValue oneline style="margin: 1em 0">
 				<template #key>securityKeys</template>
 				<template #value>{{
-					$i.securityKeys ? i18n.ts.yes : i18n.ts.no
+					me.securityKeys ? i18n.ts.yes : i18n.ts.no
 				}}</template>
 			</MkKeyValue>
 			<MkKeyValue oneline style="margin: 1em 0">
 				<template #key>usePasswordLessLogin</template>
 				<template #value>{{
-					$i.usePasswordLessLogin ? i18n.ts.yes : i18n.ts.no
+					me.usePasswordLessLogin ? i18n.ts.yes : i18n.ts.no
 				}}</template>
 			</MkKeyValue>
 			<MkKeyValue oneline style="margin: 1em 0">
@@ -179,7 +179,7 @@ import MkKeyValue from "@/components/MkKeyValue.vue";
 import * as os from "@/os";
 import number from "@/filters/number";
 import bytes from "@/filters/bytes";
-import { $i, isAdmin, isModerator } from "@/reactiveAccount";
+import { isAdmin, isModerator, me } from "@/me";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -188,7 +188,7 @@ const stats = ref<any>({});
 
 onMounted(() => {
 	os.api("users/stats", {
-		userId: $i!.id,
+		userId: me!.id,
 	}).then((response) => {
 		stats.value = response;
 	});
diff --git a/packages/client/src/pages/settings/accounts.vue b/packages/client/src/pages/settings/accounts.vue
index 2e0230d7a2..00e8050ee3 100644
--- a/packages/client/src/pages/settings/accounts.vue
+++ b/packages/client/src/pages/settings/accounts.vue
@@ -37,9 +37,9 @@ import {
 	removeAccount as _removeAccount,
 	addAccount as addAccounts,
 	getAccounts,
-	login,
+	signIn,
 } from "@/account";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -50,7 +50,7 @@ const accounts = ref<any>(null);
 const init = async () => {
 	getAccounts()
 		.then((accounts) => {
-			storedAccounts.value = accounts.filter((x) => x.id !== $i!.id);
+			storedAccounts.value = accounts.filter((x) => x.id !== me!.id);
 
 			console.log(storedAccounts.value);
 
@@ -142,7 +142,7 @@ async function switchAccount(account: any) {
 }
 
 function switchAccountWithToken(token: string) {
-	login(token);
+	signIn(token);
 }
 
 definePageMetadata({
diff --git a/packages/client/src/pages/settings/delete-account.vue b/packages/client/src/pages/settings/delete-account.vue
index f1ad8ee1b4..9d697da024 100644
--- a/packages/client/src/pages/settings/delete-account.vue
+++ b/packages/client/src/pages/settings/delete-account.vue
@@ -7,7 +7,7 @@
 			i18n.ts._accountDelete.sendEmail
 		}}</FormInfo>
 		<FormButton
-			v-if="!$i.isDeleted"
+			v-if="!me.isDeleted"
 			danger
 			class="_formBlock"
 			@click="deleteAccount"
@@ -23,7 +23,7 @@
 import FormInfo from "@/components/MkInfo.vue";
 import FormButton from "@/components/MkButton.vue";
 import * as os from "@/os";
-import { signout } from "@/account";
+import { signOut } from "@/account";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -51,7 +51,7 @@ async function deleteAccount() {
 		title: i18n.ts._accountDelete.started,
 	});
 
-	await signout();
+	await signOut();
 }
 
 definePageMetadata({
diff --git a/packages/client/src/pages/settings/drive.vue b/packages/client/src/pages/settings/drive.vue
index e78c856f37..eb279cc372 100644
--- a/packages/client/src/pages/settings/drive.vue
+++ b/packages/client/src/pages/settings/drive.vue
@@ -57,14 +57,14 @@ import bytes from "@/filters/bytes";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import icon from "@/scripts/icon";
 
 const fetching = ref(true);
 const usage = ref<any>(null);
 const capacity = ref<any>(null);
 const uploadFolder = ref<any>(null);
-const alwaysMarkNsfw = ref<boolean>(isSignedIn && $i.alwaysMarkNsfw);
+const alwaysMarkNsfw = ref<boolean>(isSignedIn && me.alwaysMarkNsfw);
 
 const meterStyle = computed(() => {
 	return {
diff --git a/packages/client/src/pages/settings/email.vue b/packages/client/src/pages/settings/email.vue
index da8578ead7..c436a6eec3 100644
--- a/packages/client/src/pages/settings/email.vue
+++ b/packages/client/src/pages/settings/email.vue
@@ -6,11 +6,11 @@
 				<template #prefix
 					><i :class="icon('ph-envelope-simple-open')"></i
 				></template>
-				<template v-if="$i.email && !$i.emailVerified" #caption>{{
+				<template v-if="me.email && !me.emailVerified" #caption>{{
 					i18n.ts.verificationEmailSent
 				}}</template>
 				<template
-					v-else-if="emailAddress === $i.email && $i.emailVerified"
+					v-else-if="emailAddress === me.email && me.emailVerified"
 					#caption
 					><i
 						:class="icon('ph-check')"
@@ -23,7 +23,7 @@
 
 		<FormSection>
 			<FormSwitch
-				:model-value="$i.receiveAnnouncementEmail"
+				:model-value="me.receiveAnnouncementEmail"
 				@update:modelValue="onChangeReceiveAnnouncementEmail"
 			>
 				{{ i18n.ts.receiveAnnouncementFromInstance }}
@@ -66,12 +66,12 @@ import FormSection from "@/components/form/section.vue";
 import FormInput from "@/components/form/input.vue";
 import FormSwitch from "@/components/form/switch.vue";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
 
-const emailAddress = ref($i!.email);
+const emailAddress = ref(me!.email);
 
 const onChangeReceiveAnnouncementEmail = (v) => {
 	os.api("i/update", {
@@ -93,22 +93,22 @@ const saveEmailAddress = () => {
 };
 
 const emailNotification_mention = ref(
-	$i!.emailNotificationTypes.includes("mention"),
+	me!.emailNotificationTypes.includes("mention"),
 );
 const emailNotification_reply = ref(
-	$i!.emailNotificationTypes.includes("reply"),
+	me!.emailNotificationTypes.includes("reply"),
 );
 const emailNotification_quote = ref(
-	$i!.emailNotificationTypes.includes("quote"),
+	me!.emailNotificationTypes.includes("quote"),
 );
 const emailNotification_follow = ref(
-	$i!.emailNotificationTypes.includes("follow"),
+	me!.emailNotificationTypes.includes("follow"),
 );
 const emailNotification_receiveFollowRequest = ref(
-	$i!.emailNotificationTypes.includes("receiveFollowRequest"),
+	me!.emailNotificationTypes.includes("receiveFollowRequest"),
 );
 const emailNotification_groupInvited = ref(
-	$i!.emailNotificationTypes.includes("groupInvited"),
+	me!.emailNotificationTypes.includes("groupInvited"),
 );
 
 const saveNotificationSettings = () => {
diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue
index 558386708a..2a7190bd6d 100644
--- a/packages/client/src/pages/settings/general.vue
+++ b/packages/client/src/pages/settings/general.vue
@@ -103,7 +103,7 @@
 				i18n.ts.enableTimelineStreaming
 			}}</FormSwitch>
 			<!-- <FormSwitch
-				v-model="$i.injectFeaturedNote"
+				v-model="me.injectFeaturedNote"
 				class="_formBlock"
 				@update:modelValue="onChangeInjectFeaturedNote"
 			>
@@ -297,7 +297,7 @@
 				}}</template></FormSwitch
 			>
 			<FormSwitch
-				v-if="$i?.isAdmin"
+				v-if="me?.isAdmin"
 				v-model="showAdminUpdates"
 				class="_formBlock"
 				>{{ i18n.ts.showAdminUpdates }}</FormSwitch
@@ -376,7 +376,7 @@
 
 <script lang="ts" setup>
 import { computed, ref, watch } from "vue";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import FormSwitch from "@/components/form/switch.vue";
 import FormSelect from "@/components/form/select.vue";
 import FormRadios from "@/components/form/radios.vue";
@@ -536,7 +536,7 @@ const showNoAltTextWarning = computed(
 // 	os.api("i/update", {
 // 		injectFeaturedNote: v,
 // 	}).then((i) => {
-// 		$i!.injectFeaturedNote = i.injectFeaturedNote;
+// 		me!.injectFeaturedNote = i.injectFeaturedNote;
 // 	});
 // }
 
diff --git a/packages/client/src/pages/settings/index.vue b/packages/client/src/pages/settings/index.vue
index 47812e5349..4954219ecb 100644
--- a/packages/client/src/pages/settings/index.vue
+++ b/packages/client/src/pages/settings/index.vue
@@ -45,8 +45,8 @@ import { computed, onActivated, onMounted, onUnmounted, ref, watch } from "vue";
 import { i18n } from "@/i18n";
 import MkInfo from "@/components/MkInfo.vue";
 import MkSuperMenu from "@/components/MkSuperMenu.vue";
-import { signout } from "@/account";
-import { $i } from "@/reactiveAccount";
+import { signOut } from "@/account";
+import { me } from "@/me";
 import { unisonReload } from "@/scripts/unison-reload";
 import { instance } from "@/instance";
 import { useRouter } from "@/router";
@@ -248,7 +248,7 @@ const menuDef = computed(() => [
 						text: i18n.ts.logoutConfirm,
 					});
 					if (canceled) return;
-					signout();
+					signOut();
 				},
 				danger: true,
 			},
@@ -291,7 +291,7 @@ watch(router.currentRef, (to) => {
 });
 
 const emailNotConfigured = computed(
-	() => instance.enableEmail && ($i.email == null || !$i.emailVerified),
+	() => instance.enableEmail && (me.email == null || !me.emailVerified),
 );
 
 provideMetadataReceiver((info) => {
diff --git a/packages/client/src/pages/settings/instance-mute.vue b/packages/client/src/pages/settings/instance-mute.vue
index f73416bbb4..43a4bb266f 100644
--- a/packages/client/src/pages/settings/instance-mute.vue
+++ b/packages/client/src/pages/settings/instance-mute.vue
@@ -26,12 +26,12 @@ import FormTextarea from "@/components/form/textarea.vue";
 import MkInfo from "@/components/MkInfo.vue";
 import MkButton from "@/components/MkButton.vue";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
 
-const instanceMutes = ref($i!.mutedInstances.join("\n"));
+const instanceMutes = ref(me!.mutedInstances.join("\n"));
 const changed = ref(false);
 
 async function save() {
diff --git a/packages/client/src/pages/settings/migration.vue b/packages/client/src/pages/settings/migration.vue
index e2a180ba06..e82ee8b1bc 100644
--- a/packages/client/src/pages/settings/migration.vue
+++ b/packages/client/src/pages/settings/migration.vue
@@ -60,7 +60,7 @@ import FormInfo from "@/components/MkInfo.vue";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import icon from "@/scripts/icon";
 
 const moveToAccount = ref("");
@@ -69,8 +69,8 @@ const accountAlias = ref([""]);
 await init();
 
 async function init() {
-	if ($i?.alsoKnownAs && $i.alsoKnownAs.length > 0) {
-		const aka = await os.api("users/show", { userIds: $i.alsoKnownAs });
+	if (me?.alsoKnownAs && me.alsoKnownAs.length > 0) {
+		const aka = await os.api("users/show", { userIds: me.alsoKnownAs });
 		accountAlias.value =
 			aka && aka.length > 0
 				? aka.map((user) => `@${acct.toString(user)}`)
@@ -86,7 +86,7 @@ async function save(): Promise<void> {
 			.map((e) => e.trim())
 			.filter((e) => e !== ""),
 	});
-	$i.alsoKnownAs = i.alsoKnownAs;
+	me.alsoKnownAs = i.alsoKnownAs;
 	await init();
 }
 
diff --git a/packages/client/src/pages/settings/notifications.vue b/packages/client/src/pages/settings/notifications.vue
index 0100451893..d410163568 100644
--- a/packages/client/src/pages/settings/notifications.vue
+++ b/packages/client/src/pages/settings/notifications.vue
@@ -54,7 +54,7 @@ import { notificationTypes } from "firefish-js";
 import FormButton from "@/components/MkButton.vue";
 import FormSection from "@/components/form/section.vue";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import MkPushNotificationAllowButton from "@/components/MkPushNotificationAllowButton.vue";
@@ -83,7 +83,7 @@ async function readAllNotifications() {
 
 function configure() {
 	const includingTypes = notificationTypes.filter(
-		(x) => !$i!.mutingNotificationTypes.includes(x),
+		(x) => !me!.mutingNotificationTypes.includes(x),
 	);
 	os.popup(
 		defineAsyncComponent(
@@ -103,7 +103,7 @@ function configure() {
 						),
 					})
 					.then((i) => {
-						$i!.mutingNotificationTypes = i.mutingNotificationTypes;
+						me!.mutingNotificationTypes = i.mutingNotificationTypes;
 					});
 			},
 		},
diff --git a/packages/client/src/pages/settings/preferences-backups.vue b/packages/client/src/pages/settings/preferences-backups.vue
index c1d4314d48..41e7dc0ad1 100644
--- a/packages/client/src/pages/settings/preferences-backups.vue
+++ b/packages/client/src/pages/settings/preferences-backups.vue
@@ -65,7 +65,7 @@ import * as os from "@/os";
 import { ColdDeviceStorage, defaultStore } from "@/store";
 import { unisonReload } from "@/scripts/unison-reload";
 import { useStream } from "@/stream";
-import { isSignedIn } from "@/reactiveAccount";
+import { isSignedIn } from "@/me";
 import { i18n } from "@/i18n";
 import { host, version } from "@/config";
 import { definePageMetadata } from "@/scripts/page-metadata";
diff --git a/packages/client/src/pages/settings/privacy.vue b/packages/client/src/pages/settings/privacy.vue
index 53ec77b1bd..e3ed64d2a0 100644
--- a/packages/client/src/pages/settings/privacy.vue
+++ b/packages/client/src/pages/settings/privacy.vue
@@ -165,19 +165,19 @@ import FormFolder from "@/components/form/folder.vue";
 import * as os from "@/os";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
 
-const isLocked = ref($i.isLocked);
-const autoAcceptFollowed = ref($i.autoAcceptFollowed);
-const noCrawle = ref($i.noCrawle);
-const isIndexable = ref($i.isIndexable);
-const isExplorable = ref($i.isExplorable);
-const hideOnlineStatus = ref($i.hideOnlineStatus);
-const publicReactions = ref($i.publicReactions);
-const ffVisibility = ref($i.ffVisibility);
-const preventAiLearning = ref($i.preventAiLearning);
+const isLocked = ref(me.isLocked);
+const autoAcceptFollowed = ref(me.autoAcceptFollowed);
+const noCrawle = ref(me.noCrawle);
+const isIndexable = ref(me.isIndexable);
+const isExplorable = ref(me.isExplorable);
+const hideOnlineStatus = ref(me.hideOnlineStatus);
+const publicReactions = ref(me.publicReactions);
+const ffVisibility = ref(me.ffVisibility);
+const preventAiLearning = ref(me.preventAiLearning);
 
 const defaultNoteVisibility = computed(
 	defaultStore.makeGetterSetter("defaultNoteVisibility"),
diff --git a/packages/client/src/pages/settings/profile.vue b/packages/client/src/pages/settings/profile.vue
index 4b06ad82fc..7f9f29d7ad 100644
--- a/packages/client/src/pages/settings/profile.vue
+++ b/packages/client/src/pages/settings/profile.vue
@@ -7,13 +7,13 @@
 		<div
 			class="llvierxe"
 			:style="{
-				backgroundImage: $i.bannerUrl ? `url(${$i.bannerUrl})` : null,
+				backgroundImage: me.bannerUrl ? `url(${me.bannerUrl})` : null,
 			}"
 		>
 			<div class="avatar">
 				<MkAvatar
 					class="avatar"
-					:user="$i"
+					:user="me"
 					:disable-link="true"
 					@click="changeAvatar"
 				/>
@@ -118,7 +118,7 @@
 				i18n.t("_profile.metadataDescription", {
 					a: "\<a\>",
 					l: "\<a\>",
-					rel: `rel="me" href="https://${host}/@${$i.username}"`,
+					rel: `rel="me" href="https://${host}/@${me.username}"`,
 				})
 			}}</template>
 		</FormSlot>
@@ -162,19 +162,19 @@ import FormSlot from "@/components/form/slot.vue";
 import { selectFile } from "@/scripts/select-file";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { host } from "@/config";
 import icon from "@/scripts/icon";
 
 const profile = reactive({
-	name: $i?.name,
-	description: $i?.description,
-	location: $i?.location,
-	birthday: $i?.birthday,
-	isBot: $i?.isBot,
-	isCat: $i?.isCat,
-	speakAsCat: $i?.speakAsCat,
+	name: me?.name,
+	description: me?.description,
+	location: me?.location,
+	birthday: me?.birthday,
+	isBot: me?.isBot,
+	isCat: me?.isCat,
+	speakAsCat: me?.speakAsCat,
 });
 
 const props = withDefaults(
@@ -197,7 +197,7 @@ watch(
 );
 
 const fields = reactive(
-	$i.fields.map((field) => ({ name: field.name, value: field.value })),
+	me.fields.map((field) => ({ name: field.name, value: field.value })),
 );
 
 function addField() {
@@ -253,8 +253,8 @@ function changeAvatar(ev) {
 			const i = await os.apiWithDialog("i/update", {
 				avatarId: originalOrCropped.id,
 			});
-			$i.avatarId = i.avatarId;
-			$i.avatarUrl = i.avatarUrl;
+			me.avatarId = i.avatarId;
+			me.avatarUrl = i.avatarUrl;
 		},
 	);
 }
@@ -278,8 +278,8 @@ function changeBanner(ev) {
 			const i = await os.apiWithDialog("i/update", {
 				bannerId: originalOrCropped.id,
 			});
-			$i.bannerId = i.bannerId;
-			$i.bannerUrl = i.bannerUrl;
+			me.bannerId = i.bannerId;
+			me.bannerUrl = i.bannerUrl;
 		},
 	);
 }
diff --git a/packages/client/src/pages/settings/word-mute.vue b/packages/client/src/pages/settings/word-mute.vue
index 8a6a0408ac..7646ef9385 100644
--- a/packages/client/src/pages/settings/word-mute.vue
+++ b/packages/client/src/pages/settings/word-mute.vue
@@ -74,7 +74,7 @@ import MkTab from "@/components/MkTab.vue";
 import * as os from "@/os";
 import number from "@/filters/number";
 import { defaultStore } from "@/store";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -93,8 +93,8 @@ const render = (mutedWords) =>
 const tab = ref("soft");
 const softMutedWords = ref(render(defaultStore.state.mutedWords));
 const softMutedLangs = ref(render(defaultStore.state.mutedLangs));
-const hardMutedWords = ref(render($i!.mutedWords));
-const hardMutedPatterns = ref($i!.mutedPatterns.join("\n"));
+const hardMutedWords = ref(render(me!.mutedWords));
+const hardMutedPatterns = ref(me!.mutedPatterns.join("\n"));
 const hardWordMutedNotesCount = ref(null);
 const changed = ref(false);
 
diff --git a/packages/client/src/pages/signup-complete.vue b/packages/client/src/pages/signup-complete.vue
index de6f40ffb3..7bd75adb4e 100644
--- a/packages/client/src/pages/signup-complete.vue
+++ b/packages/client/src/pages/signup-complete.vue
@@ -7,7 +7,7 @@
 <script lang="ts" setup>
 import { onMounted } from "vue";
 import * as os from "@/os";
-import { login } from "@/account";
+import { signIn } from "@/account";
 import { i18n } from "@/i18n";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import icon from "@/scripts/icon";
@@ -24,7 +24,7 @@ onMounted(async () => {
 	const res = await os.apiWithDialog("signup-pending", {
 		code: props.code,
 	});
-	login(res.i, "/");
+	signIn(res.i, "/");
 });
 
 definePageMetadata({
diff --git a/packages/client/src/pages/theme-editor.vue b/packages/client/src/pages/theme-editor.vue
index 8c0570641e..3886113105 100644
--- a/packages/client/src/pages/theme-editor.vue
+++ b/packages/client/src/pages/theme-editor.vue
@@ -151,7 +151,7 @@ import FormButton from "@/components/MkButton.vue";
 import FormTextarea from "@/components/form/textarea.vue";
 import FormFolder from "@/components/form/folder.vue";
 
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import type { Theme } from "@/scripts/theme";
 import { applyTheme } from "@/scripts/theme";
 import lightTheme from "@/themes/_light.json5";
@@ -315,7 +315,7 @@ async function saveAs() {
 
 	theme.value.id = uuid();
 	theme.value.name = name;
-	theme.value.author = `@${$i.username}@${toUnicode(host)}`;
+	theme.value.author = `@${me.username}@${toUnicode(host)}`;
 	if (description.value) theme.value.desc = description.value;
 	await addTheme(theme.value);
 	applyTheme(theme.value);
diff --git a/packages/client/src/pages/timeline.vue b/packages/client/src/pages/timeline.vue
index e2aeca8917..8a577e78ce 100644
--- a/packages/client/src/pages/timeline.vue
+++ b/packages/client/src/pages/timeline.vue
@@ -75,7 +75,7 @@ import * as os from "@/os";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
 import { instance } from "@/instance";
-import { isModerator, isSignedIn } from "@/reactiveAccount";
+import { isModerator, isSignedIn } from "@/me";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { deviceKind } from "@/scripts/device-kind";
 import icon from "@/scripts/icon";
diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue
index 20f9b8a5d2..2fa62e8258 100644
--- a/packages/client/src/pages/user-info.vue
+++ b/packages/client/src/pages/user-info.vue
@@ -272,17 +272,16 @@
 						manual-save
 						class="_formBlock"
 					>
-						<template #label>Moderation note</template>
+						<template #label>{{ i18n.ts.moderationNote }}</template>
 					</FormTextarea>
 					<FormFolder class="_formBlock">
 						<template #label>IP</template>
 						<MkInfo v-if="!isAdmin" warn>{{
 							i18n.ts.requireAdminForView
 						}}</MkInfo>
-						<MkInfo v-else
-							>The date is the IP address was first
-							acknowledged.</MkInfo
-						>
+						<MkInfo v-else>{{
+							i18n.ts.ipFirstAcknowledged
+						}}</MkInfo>
 						<template v-if="isAdmin && ips">
 							<div
 								v-for="record in ips"
@@ -305,7 +304,9 @@
 						/>
 					</FormFolder>
 					<FormSection>
-						<template #label>Drive Capacity Override</template>
+						<template #label>{{
+							i18n.ts.driveCapacityOverride
+						}}</template>
 
 						<FormInput
 							v-if="user.host == null"
@@ -362,7 +363,7 @@ import { url } from "@/config";
 import { userPage } from "@/filters/user";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { i18n } from "@/i18n";
-import { isAdmin, isModerator } from "@/reactiveAccount";
+import { isAdmin, isModerator } from "@/me";
 import { instance } from "@/instance";
 import icon from "@/scripts/icon";
 
diff --git a/packages/client/src/pages/user/home.vue b/packages/client/src/pages/user/home.vue
index 18325d121e..13846e42d4 100644
--- a/packages/client/src/pages/user/home.vue
+++ b/packages/client/src/pages/user/home.vue
@@ -68,7 +68,7 @@
 									<span
 										v-if="
 											isSignedIn &&
-											$i.id !== user.id &&
+											me.id !== user.id &&
 											user.isFollowed
 										"
 										class="followed"
@@ -133,7 +133,7 @@
 								<span
 									v-if="
 										isSignedIn &&
-										$i.id !== user.id &&
+										me.id !== user.id &&
 										user.isFollowed
 									"
 									class="followed"
@@ -221,7 +221,7 @@
 								:text="user.description"
 								:is-note="false"
 								:author="user"
-								:i="$i"
+								:i="me"
 								:custom-emojis="user.emojis"
 							/>
 							<p v-else class="empty">
@@ -294,7 +294,7 @@
 									<Mfm
 										:text="field.value"
 										:author="user"
-										:i="$i"
+										:i="me"
 										:custom-emojis="user.emojis"
 										:colored="false"
 									/>
@@ -311,6 +311,7 @@
 								<span>{{ i18n.ts.notes }}</span>
 							</MkA>
 							<MkA
+								v-if="user.followingCount != null"
 								v-click-anime
 								:to="userPage(user, 'following')"
 								:class="{ active: page === 'following' }"
@@ -319,6 +320,7 @@
 								<span>{{ i18n.ts.following }}</span>
 							</MkA>
 							<MkA
+								v-if="user.followersCount != null"
 								v-click-anime
 								:to="userPage(user, 'followers')"
 								:class="{ active: page === 'followers' }"
@@ -341,7 +343,7 @@
 						/>
 					</div>
 					<MkInfo
-						v-else-if="isSignedIn && $i.id === user.id"
+						v-else-if="isSignedIn && me.id === user.id"
 						style="margin: 12px 0"
 						>{{ i18n.ts.userPagePinTip }}</MkInfo
 					>
@@ -384,7 +386,7 @@ import { userPage } from "@/filters/user";
 import { defaultStore } from "@/store";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
-import { $i, isModerator, isSignedIn } from "@/reactiveAccount";
+import { isModerator, isSignedIn, me } from "@/me";
 import { host } from "@/config";
 import icon from "@/scripts/icon";
 
diff --git a/packages/client/src/pages/user/index.vue b/packages/client/src/pages/user/index.vue
index c0346dd0d8..6ce00f5a10 100644
--- a/packages/client/src/pages/user/index.vue
+++ b/packages/client/src/pages/user/index.vue
@@ -35,7 +35,7 @@ import * as os from "@/os";
 import { useRouter } from "@/router";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { i18n } from "@/i18n";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import icon from "@/scripts/icon";
 
 const XHome = defineAsyncComponent(() => import("./home.vue"));
@@ -86,7 +86,7 @@ const headerTabs = computed(() =>
 					title: i18n.ts.overview,
 					icon: `${icon("ph-user")}`,
 				},
-				...((isSignedIn && $i.id === user.value.id) ||
+				...((isSignedIn && me.id === user.value.id) ||
 				user.value.publicReactions
 					? [
 							{
diff --git a/packages/client/src/pages/welcome.setup.vue b/packages/client/src/pages/welcome.setup.vue
index b4a9137a3d..199c48d453 100644
--- a/packages/client/src/pages/welcome.setup.vue
+++ b/packages/client/src/pages/welcome.setup.vue
@@ -46,7 +46,7 @@ import MkButton from "@/components/MkButton.vue";
 import MkInput from "@/components/form/input.vue";
 import { host } from "@/config";
 import * as os from "@/os";
-import { login } from "@/account";
+import { signIn } from "@/account";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
@@ -71,7 +71,7 @@ function submit() {
 					});
 				}
 			});
-			return login(res?.token);
+			return signIn(res?.token);
 		})
 		.catch(() => {
 			submitting.value = false;
diff --git a/packages/client/src/pages/welcome.timeline.vue b/packages/client/src/pages/welcome.timeline.vue
index eeb5ac7170..3da8fe94d0 100644
--- a/packages/client/src/pages/welcome.timeline.vue
+++ b/packages/client/src/pages/welcome.timeline.vue
@@ -14,7 +14,7 @@
 							v-if="note.text"
 							:text="note.text"
 							:author="note.user"
-							:i="$i"
+							:i="me"
 							:custom-emojis="note.emojis"
 						/>
 						<!-- <MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA> -->
diff --git a/packages/client/src/pizzax.ts b/packages/client/src/pizzax.ts
index 5ee06d0b8d..eef73e3426 100644
--- a/packages/client/src/pizzax.ts
+++ b/packages/client/src/pizzax.ts
@@ -4,7 +4,7 @@ import type { Ref } from "vue";
 import { onUnmounted, ref, watch } from "vue";
 import { api } from "./os";
 import { useStream } from "./stream";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 
 type StateDef = Record<
 	string,
@@ -45,12 +45,12 @@ export class Storage<T extends StateDef> {
 		);
 		const deviceAccountState = isSignedIn
 			? JSON.parse(
-					localStorage.getItem(`${this.keyForLocalStorage}::${$i.id}`) || "{}",
+					localStorage.getItem(`${this.keyForLocalStorage}::${me.id}`) || "{}",
 			  )
 			: {};
 		const registryCache = isSignedIn
 			? JSON.parse(
-					localStorage.getItem(`${this.keyForLocalStorage}::cache::${$i.id}`) ||
+					localStorage.getItem(`${this.keyForLocalStorage}::cache::${me.id}`) ||
 						"{}",
 			  )
 			: {};
@@ -104,7 +104,7 @@ export class Storage<T extends StateDef> {
 							}
 						}
 						localStorage.setItem(
-							`${this.keyForLocalStorage}::cache::${$i.id}`,
+							`${this.keyForLocalStorage}::cache::${me.id}`,
 							JSON.stringify(cache),
 						);
 					},
@@ -135,13 +135,13 @@ export class Storage<T extends StateDef> {
 
 					const cache = JSON.parse(
 						localStorage.getItem(
-							`${this.keyForLocalStorage}::cache::${$i.id}`,
+							`${this.keyForLocalStorage}::cache::${me.id}`,
 						) || "{}",
 					);
 					if (cache[key] !== value) {
 						cache[key] = value;
 						localStorage.setItem(
-							`${this.keyForLocalStorage}::cache::${$i.id}`,
+							`${this.keyForLocalStorage}::cache::${me.id}`,
 							JSON.stringify(cache),
 						);
 					}
@@ -171,11 +171,11 @@ export class Storage<T extends StateDef> {
 			case "deviceAccount": {
 				if (!isSignedIn) break;
 				const deviceAccountState = JSON.parse(
-					localStorage.getItem(`${this.keyForLocalStorage}::${$i.id}`) || "{}",
+					localStorage.getItem(`${this.keyForLocalStorage}::${me.id}`) || "{}",
 				);
 				deviceAccountState[key] = value;
 				localStorage.setItem(
-					`${this.keyForLocalStorage}::${$i.id}`,
+					`${this.keyForLocalStorage}::${me.id}`,
 					JSON.stringify(deviceAccountState),
 				);
 				break;
@@ -183,12 +183,12 @@ export class Storage<T extends StateDef> {
 			case "account": {
 				if (!isSignedIn) break;
 				const cache = JSON.parse(
-					localStorage.getItem(`${this.keyForLocalStorage}::cache::${$i.id}`) ||
+					localStorage.getItem(`${this.keyForLocalStorage}::cache::${me.id}`) ||
 						"{}",
 				);
 				cache[key] = value;
 				localStorage.setItem(
-					`${this.keyForLocalStorage}::cache::${$i.id}`,
+					`${this.keyForLocalStorage}::cache::${me.id}`,
 					JSON.stringify(cache),
 				);
 				api("i/registry/set", {
diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts
index ffc4c29ac1..cf7ccf516e 100644
--- a/packages/client/src/router.ts
+++ b/packages/client/src/router.ts
@@ -1,9 +1,9 @@
 import type { AsyncComponentLoader } from "vue";
 import { defineAsyncComponent, inject } from "vue";
+import { isEmojiMod, isModerator, me } from "@/me";
 import { Router } from "@/nirax";
 import MkError from "@/pages/_error_.vue";
 import MkLoading from "@/pages/_loading_.vue";
-import { $i, isEmojiMod, isModerator } from "@/reactiveAccount";
 
 const page = (loader: AsyncComponentLoader<any>) =>
 	defineAsyncComponent({
@@ -657,7 +657,7 @@ export const routes = [
 	{
 		name: "index",
 		path: "/",
-		component: $i
+		component: me
 			? page(() => import("./pages/timeline.vue"))
 			: page(() => import("./pages/welcome.vue")),
 		globalCacheKey: "index",
diff --git a/packages/client/src/scripts/aiscript/api.ts b/packages/client/src/scripts/aiscript/api.ts
index e0eed05c02..1e6b7aa018 100644
--- a/packages/client/src/scripts/aiscript/api.ts
+++ b/packages/client/src/scripts/aiscript/api.ts
@@ -1,13 +1,13 @@
 import { utils, values } from "@syuilo/aiscript";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 
 export function createAiScriptEnv(opts) {
 	let apiRequests = 0;
 	return {
-		USER_ID: $i ? values.STR($i.id) : values.NULL,
-		USER_NAME: $i ? values.STR($i.name) : values.NULL,
-		USER_USERNAME: $i ? values.STR($i.username) : values.NULL,
+		USER_ID: me ? values.STR(me.id) : values.NULL,
+		USER_NAME: me ? values.STR(me.name) : values.NULL,
+		USER_USERNAME: me ? values.STR(me.username) : values.NULL,
 		"Mk:dialog": values.FN_NATIVE(async ([title, text, type]) => {
 			await os.alert({
 				type: type ? type.value : "info",
diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts
index 6713b02d96..ddd9d7c5f4 100644
--- a/packages/client/src/scripts/get-note-menu.ts
+++ b/packages/client/src/scripts/get-note-menu.ts
@@ -1,7 +1,7 @@
 import type { Ref } from "vue";
 import { defineAsyncComponent } from "vue";
 import type { entities } from "firefish-js";
-import { $i, isModerator, isSignedIn } from "@/reactiveAccount";
+import { isModerator, isSignedIn, me } from "@/me";
 import { i18n } from "@/i18n";
 import { instance } from "@/instance";
 import * as os from "@/os";
@@ -288,10 +288,10 @@ export function getNoteMenu(props: {
 			noteId: appearNote.id,
 		});
 
-		const isAppearAuthor = appearNote.userId === $i.id;
+		const isAppearAuthor = appearNote.userId === me.id;
 
 		menu = [
-			...(props.currentClipPage?.value.userId === $i.id
+			...(props.currentClipPage?.value.userId === me.id
 				? [
 						{
 							icon: `${icon("ph-minus-circle")}`,
@@ -349,7 +349,7 @@ export function getNoteMenu(props: {
 					  },
 			),
 			isAppearAuthor
-				? ($i.pinnedNoteIds || []).includes(appearNote.id)
+				? (me.pinnedNoteIds || []).includes(appearNote.id)
 					? {
 							icon: `${icon("ph-push-pin")}`,
 							text: i18n.ts.unpin,
diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts
index 5df9809a98..e83201e6da 100644
--- a/packages/client/src/scripts/get-user-menu.ts
+++ b/packages/client/src/scripts/get-user-menu.ts
@@ -5,7 +5,7 @@ import copyToClipboard from "@/scripts/copy-to-clipboard";
 import { host } from "@/config";
 import * as os from "@/os";
 import { userActions } from "@/store";
-import { $i, isModerator, isSignedIn } from "@/reactiveAccount";
+import { isModerator, isSignedIn, me } from "@/me";
 import { mainRouter } from "@/router";
 import type { Router } from "@/nirax";
 import icon from "@/scripts/icon";
@@ -290,7 +290,7 @@ export function getUserMenu(user, router: Router = mainRouter) {
 				os.post({ specified: user });
 			},
 		},
-		$i.id !== user.id
+		me.id !== user.id
 			? {
 					type: "link",
 					icon: `${icon("ph-chats-teardrop")}`,
@@ -313,7 +313,7 @@ export function getUserMenu(user, router: Router = mainRouter) {
 			text: i18n.ts.addToList,
 			action: pushList,
 		},
-		$i.id !== user.id
+		me.id !== user.id
 			? {
 					icon: `${icon("ph-users-three")}`,
 					text: i18n.ts.inviteToGroup,
@@ -335,7 +335,7 @@ export function getUserMenu(user, router: Router = mainRouter) {
 		},
 	] as any;
 
-	if (isSignedIn && $i.id !== user.id) {
+	if (isSignedIn && me.id !== user.id) {
 		menu = menu.concat([
 			{
 				icon: user.isMuted ? "ph-eye ph-lg" : "ph-eye-slash ph-lg",
@@ -386,7 +386,7 @@ export function getUserMenu(user, router: Router = mainRouter) {
 		}
 	}
 
-	if (isSignedIn && $i.id === user.id) {
+	if (isSignedIn && me.id === user.id) {
 		menu = menu.concat([
 			null,
 			{
diff --git a/packages/client/src/scripts/i18n.ts b/packages/client/src/scripts/i18n.ts
deleted file mode 100644
index 7e2b237118..0000000000
--- a/packages/client/src/scripts/i18n.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-export class I18n<T extends Record<string, any>> {
-	public ts: T;
-
-	constructor(locale: T) {
-		this.ts = locale;
-
-		// #region BIND
-		this.t = this.t.bind(this);
-		// #endregion
-	}
-
-	// string にしているのは、ドット区切りでのパス指定を許可するため
-	// なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
-	public t(key: string, args?: Record<string, string | number>): string {
-		try {
-			let str = key
-				.split(".")
-				.reduce((o, i) => o[i], this.ts) as unknown as string;
-
-			if (args) {
-				for (const [k, v] of Object.entries(args)) {
-					str = str.replace(`{${k}}`, v.toString());
-				}
-			}
-			return str;
-		} catch (err) {
-			console.warn(`missing localization '${key}'`);
-			return key;
-		}
-	}
-}
diff --git a/packages/client/src/scripts/please-login.ts b/packages/client/src/scripts/please-login.ts
index 8d4f466fc4..b66802b816 100644
--- a/packages/client/src/scripts/please-login.ts
+++ b/packages/client/src/scripts/please-login.ts
@@ -1,5 +1,5 @@
 import { defineAsyncComponent } from "vue";
-import { isSignedIn } from "@/reactiveAccount";
+import { isSignedIn } from "@/me";
 import { i18n } from "@/i18n";
 import { popup } from "@/os";
 import { vibrate } from "@/scripts/vibrate";
diff --git a/packages/client/src/scripts/upload.ts b/packages/client/src/scripts/upload.ts
index 4ea4bd648c..c13f28cd6e 100644
--- a/packages/client/src/scripts/upload.ts
+++ b/packages/client/src/scripts/upload.ts
@@ -3,7 +3,7 @@ import type { entities } from "firefish-js";
 import { readAndCompressImage } from "browser-image-resizer";
 import { defaultStore } from "@/store";
 import { apiUrl } from "@/config";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { alert } from "@/os";
 import { i18n } from "@/i18n";
 
@@ -84,7 +84,7 @@ export function uploadFile(
 
 			const xhr = new XMLHttpRequest();
 			xhr.open("POST", `${apiUrl}/drive/files/create`, true);
-			xhr.setRequestHeader("Authorization", `Bearer ${$i.token}`);
+			xhr.setRequestHeader("Authorization", `Bearer ${me.token}`);
 			xhr.onload = (ev) => {
 				if (
 					xhr.status !== 200 ||
diff --git a/packages/client/src/scripts/use-note-capture.ts b/packages/client/src/scripts/use-note-capture.ts
index 845ee30841..b8418b1ff2 100644
--- a/packages/client/src/scripts/use-note-capture.ts
+++ b/packages/client/src/scripts/use-note-capture.ts
@@ -2,7 +2,7 @@ import type { Ref } from "vue";
 import { onUnmounted } from "vue";
 import type { entities } from "firefish-js";
 import { useStream } from "@/stream";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import * as os from "@/os";
 
 export function useNoteCapture(props: {
@@ -34,7 +34,7 @@ export function useNoteCapture(props: {
 
 				note.value.reactions[reaction] = currentCount + 1;
 
-				if (isSignedIn && body.userId === $i.id) {
+				if (isSignedIn && body.userId === me.id) {
 					note.value.myReaction = reaction;
 				}
 				break;
@@ -48,7 +48,7 @@ export function useNoteCapture(props: {
 
 				note.value.reactions[reaction] = Math.max(0, currentCount - 1);
 
-				if (isSignedIn && body.userId === $i.id) {
+				if (isSignedIn && body.userId === me.id) {
 					note.value.myReaction = undefined;
 				}
 				break;
@@ -62,7 +62,7 @@ export function useNoteCapture(props: {
 					choices[choice] = {
 						...choices[choice],
 						votes: choices[choice].votes + 1,
-						...(isSignedIn && body.userId === $i.id
+						...(isSignedIn && body.userId === me.id
 							? {
 									isVoted: true,
 							  }
diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts
index 33f3fc6e2c..6e7e4b8570 100644
--- a/packages/client/src/store.ts
+++ b/packages/client/src/store.ts
@@ -1,6 +1,6 @@
 import { markRaw, ref } from "vue";
+import { isSignedIn } from "./me";
 import { Storage } from "./pizzax";
-import { isSignedIn } from "./reactiveAccount";
 
 export const postFormActions = [];
 export const userActions = [];
diff --git a/packages/client/src/stream.ts b/packages/client/src/stream.ts
index 1de6810383..0e705baad5 100644
--- a/packages/client/src/stream.ts
+++ b/packages/client/src/stream.ts
@@ -1,7 +1,7 @@
 import { Stream } from "firefish-js";
 import { markRaw } from "vue";
 import { url } from "@/config";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 
 let stream: Stream | null = null;
 let timeoutHeartBeat: number | null = null;
@@ -13,9 +13,9 @@ export function useStream() {
 	stream = markRaw(
 		new Stream(
 			url,
-			$i
+			me
 				? {
-						token: $i.token,
+						token: me.token,
 				  }
 				: null,
 		),
diff --git a/packages/client/src/theme-store.ts b/packages/client/src/theme-store.ts
index 794502da26..a5957dea8f 100644
--- a/packages/client/src/theme-store.ts
+++ b/packages/client/src/theme-store.ts
@@ -1,8 +1,8 @@
 import type { Theme } from "./scripts/theme";
+import { isSignedIn, me } from "@/me";
 import { api } from "@/os";
-import { $i, isSignedIn } from "@/reactiveAccount";
 
-const lsCacheKey = isSignedIn ? `themes:${$i.id}` : "";
+const lsCacheKey = isSignedIn ? `themes:${me.id}` : "";
 
 export function getThemes(): Theme[] {
 	return JSON.parse(localStorage.getItem(lsCacheKey) || "[]");
diff --git a/packages/client/src/ui/_common_/common.vue b/packages/client/src/ui/_common_/common.vue
index 0d35d38f47..b4f20869b8 100644
--- a/packages/client/src/ui/_common_/common.vue
+++ b/packages/client/src/ui/_common_/common.vue
@@ -22,7 +22,7 @@ import { swInject } from "./sw-inject";
 import { popup, popups } from "@/os";
 import { uploads } from "@/scripts/upload";
 import * as sound from "@/scripts/sound";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import { useStream } from "@/stream";
 
 const stream = useStream();
@@ -35,7 +35,7 @@ const XUpload = defineAsyncComponent(() => import("./upload.vue"));
 const dev = _DEV_;
 
 const onNotification = (notification) => {
-	if ($i.mutingNotificationTypes.includes(notification.type)) return;
+	if (me.mutingNotificationTypes.includes(notification.type)) return;
 
 	if (document.visibilityState === "visible") {
 		stream.send("readNotification", {
diff --git a/packages/client/src/ui/_common_/navbar-for-mobile.vue b/packages/client/src/ui/_common_/navbar-for-mobile.vue
index e7adb8d3eb..bf14fb8c2b 100644
--- a/packages/client/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/client/src/ui/_common_/navbar-for-mobile.vue
@@ -4,22 +4,22 @@
 			<div class="top">
 				<div
 					class="banner"
-					:user="$i"
-					:style="{ backgroundImage: `url(${$i.bannerUrl})` }"
+					:user="me"
+					:style="{ backgroundImage: `url(${me.bannerUrl})` }"
 				></div>
 				<button
 					v-click-anime
 					v-tooltip.noDelay.right="
-						`${i18n.ts.account}: @${$i.username}`
+						`${i18n.ts.account}: @${me.username}`
 					"
 					class="item _button account"
 					@click="openAccountMenu"
 				>
 					<MkAvatar
-						:user="$i"
+						:user="me"
 						class="icon"
 						disable-link
-					/><!-- <MkAcct class="text" :user="$i"/> -->
+					/><!-- <MkAcct class="text" :user="me"/> -->
 				</button>
 			</div>
 			<div class="middle">
@@ -81,7 +81,7 @@
 					><span class="text">{{ i18n.ts.controlPanel }}</span>
 				</MkA>
 				<MkA
-					v-else-if="$i.emojiModPerm !== 'unauthorized'"
+					v-else-if="me.emojiModPerm !== 'unauthorized'"
 					v-click-anime
 					v-tooltip.noDelay.right="i18n.ts.customEmojis"
 					class="item _button"
@@ -141,7 +141,7 @@ import { computed, defineAsyncComponent, toRef } from "vue";
 import * as os from "@/os";
 import { navbarItemDef } from "@/navbar";
 import { openAccountMenu as openAccountMenu_ } from "@/account";
-import { $i, isModerator } from "@/reactiveAccount";
+import { isModerator, me } from "@/me";
 import { openHelpMenu_ } from "@/scripts/helpMenu";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue
index ad9d1bf206..e2937528de 100644
--- a/packages/client/src/ui/_common_/navbar.vue
+++ b/packages/client/src/ui/_common_/navbar.vue
@@ -4,22 +4,22 @@
 			<div class="top">
 				<div
 					class="banner"
-					:user="$i"
-					:style="{ backgroundImage: `url(${$i.bannerUrl})` }"
+					:user="me"
+					:style="{ backgroundImage: `url(${me.bannerUrl})` }"
 				></div>
 				<button
 					v-click-anime
 					v-tooltip.noDelay.right="
-						`${i18n.ts.account}: @${$i.username}`
+						`${i18n.ts.account}: @${me.username}`
 					"
 					class="item _button account"
 					@click="openAccountMenu"
 				>
 					<MkAvatar
-						:user="$i"
+						:user="me"
 						class="icon"
 						disable-link
-					/><!-- <MkAcct class="text" :user="$i"/> -->
+					/><!-- <MkAcct class="text" :user="me"/> -->
 				</button>
 			</div>
 			<nav class="middle">
@@ -93,7 +93,7 @@
 					><span class="text">{{ i18n.ts.controlPanel }}</span>
 				</MkA>
 				<MkA
-					v-else-if="$i.emojiModPerm !== 'unauthorized'"
+					v-else-if="me.emojiModPerm !== 'unauthorized'"
 					v-click-anime
 					v-tooltip.noDelay.right="i18n.ts.customEmojis"
 					class="item _button"
@@ -145,12 +145,6 @@
 						:class="icon('help icon ph-info ph-xl ph-fw', false)"
 					></i>
 				</button>
-				<!-- <button v-click-anime v-tooltip.noDelay.right="$instance.name ?? i18n.ts.instance" class="item _button instance" @click="openInstanceMenu">
-				<img :src="$instance.faviconUrl || $instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
-			</button> -->
-				<!-- <button v-click-anime v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="item _button account" @click="openAccountMenu">
-				<MkAvatar :user="$i" class="account"/><MkAcct class="text" :user="$i"/>
-			</button> -->
 			</div>
 		</div>
 	</header>
@@ -161,7 +155,7 @@ import { computed, defineAsyncComponent, ref, watch } from "vue";
 import * as os from "@/os";
 import { navbarItemDef } from "@/navbar";
 import { openAccountMenu as openAccountMenu_ } from "@/account";
-import { $i, isAdmin, isModerator } from "@/reactiveAccount";
+import { isAdmin, isModerator, me } from "@/me";
 import { openHelpMenu_ } from "@/scripts/helpMenu";
 import { defaultStore } from "@/store";
 import { i18n } from "@/i18n";
diff --git a/packages/client/src/ui/_common_/sw-inject.ts b/packages/client/src/ui/_common_/sw-inject.ts
index afc1aee8f6..17974674ac 100644
--- a/packages/client/src/ui/_common_/sw-inject.ts
+++ b/packages/client/src/ui/_common_/sw-inject.ts
@@ -1,6 +1,6 @@
 import { post } from "@/os";
-import { login } from "@/account";
-import { $i } from "@/reactiveAccount";
+import { signIn } from "@/account";
+import { me } from "@/me";
 import { getAccountFromId } from "@/scripts/get-account-from-id";
 import { mainRouter } from "@/router";
 
@@ -12,10 +12,10 @@ export function swInject() {
 
 		if (ev.data.type !== "order") return;
 
-		if (ev.data.loginId !== $i?.id) {
+		if (ev.data.loginId !== me?.id) {
 			return getAccountFromId(ev.data.loginId).then((account) => {
 				if (!account) return;
-				return login(account.token, ev.data.url);
+				return signIn(account.token, ev.data.url);
 			});
 		}
 
diff --git a/packages/client/src/ui/deck.vue b/packages/client/src/ui/deck.vue
index 5b020c48c2..1f345e7b95 100644
--- a/packages/client/src/ui/deck.vue
+++ b/packages/client/src/ui/deck.vue
@@ -151,7 +151,7 @@
 			>
 				<i :class="icon('ph-bell')"></i
 				><span
-					v-if="$i?.hasUnreadNotification"
+					v-if="me?.hasUnreadNotification"
 					class="indicator"
 					:class="{
 						animateIndicator: defaultStore.state.animation,
@@ -203,7 +203,7 @@ import XDrawerMenu from "@/ui/_common_/navbar-for-mobile.vue";
 import MkButton from "@/components/MkButton.vue";
 import * as os from "@/os";
 import { navbarItemDef } from "@/navbar";
-import { $i, isSignedIn } from "@/reactiveAccount";
+import { isSignedIn, me } from "@/me";
 import { i18n } from "@/i18n";
 import { mainRouter } from "@/router";
 import { unisonReload } from "@/scripts/unison-reload";
diff --git a/packages/client/src/ui/deck/tl-column.vue b/packages/client/src/ui/deck/tl-column.vue
index 35debb1975..ae26489b2a 100644
--- a/packages/client/src/ui/deck/tl-column.vue
+++ b/packages/client/src/ui/deck/tl-column.vue
@@ -50,7 +50,7 @@ import type { Column } from "./deck-store";
 import { removeColumn, updateColumn } from "./deck-store";
 import XTimeline from "@/components/MkTimeline.vue";
 import * as os from "@/os";
-import { isModerator, isSignedIn } from "@/reactiveAccount";
+import { isModerator, isSignedIn } from "@/me";
 import { instance } from "@/instance";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
diff --git a/packages/client/src/ui/universal.vue b/packages/client/src/ui/universal.vue
index 229221ee2b..93cee4183a 100644
--- a/packages/client/src/ui/universal.vue
+++ b/packages/client/src/ui/universal.vue
@@ -85,7 +85,7 @@
 				>
 					<i :class="icon('ph-bell')"></i
 					><span
-						v-if="$i?.hasUnreadNotification"
+						v-if="me?.hasUnreadNotification"
 						class="indicator"
 						:class="{
 							animateIndicator: defaultStore.state.animation,
@@ -120,7 +120,7 @@
 				>
 					<i :class="icon('ph-chats-teardrop')"></i
 					><span
-						v-if="$i?.hasUnreadMessagingMessage"
+						v-if="me?.hasUnreadMessagingMessage"
 						class="indicator"
 						:class="{
 							animateIndicator: defaultStore.state.animation,
@@ -222,7 +222,7 @@ import { defaultStore } from "@/store";
 import { navbarItemDef } from "@/navbar";
 import { i18n } from "@/i18n";
 import { openAccountMenu as openAccountMenuImpl } from "@/account";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { mainRouter } from "@/router";
 import { provideMetadataReceiver } from "@/scripts/page-metadata";
 import { deviceKind } from "@/scripts/device-kind";
diff --git a/packages/client/src/ui/visitor/a.vue b/packages/client/src/ui/visitor/a.vue
index bc8f601457..1010d3a052 100644
--- a/packages/client/src/ui/visitor/a.vue
+++ b/packages/client/src/ui/visitor/a.vue
@@ -3,7 +3,7 @@
 		<div
 			v-if="mainRouter.currentRoute?.name === 'index'"
 			class="banner"
-			:style="{ backgroundImage: `url(${$instance.bannerUrl})` }"
+			:style="{ backgroundImage: `url(${instance.bannerUrl})` }"
 		>
 			<div>
 				<h1 v-if="meta">
@@ -32,7 +32,7 @@
 		<div
 			v-else
 			class="banner-mini"
-			:style="{ backgroundImage: `url(${$instance.bannerUrl})` }"
+			:style="{ backgroundImage: `url(${instance.bannerUrl})` }"
 		>
 			<div>
 				<h1 v-if="meta">
@@ -86,6 +86,7 @@ import MkButton from "@/components/MkButton.vue";
 import { ColdDeviceStorage, defaultStore } from "@/store";
 import { mainRouter } from "@/router";
 import { i18n } from "@/i18n";
+import { instance } from "@/instance";
 
 const DESKTOP_THRESHOLD = 1100;
 
diff --git a/packages/client/src/ui/visitor/kanban.vue b/packages/client/src/ui/visitor/kanban.vue
index 2a53a2e070..3b523e2110 100644
--- a/packages/client/src/ui/visitor/kanban.vue
+++ b/packages/client/src/ui/visitor/kanban.vue
@@ -4,7 +4,7 @@
 		:style="{
 			backgroundImage: transparent
 				? 'none'
-				: `url(${$instance.backgroundImageUrl})`,
+				: `url(${instance.backgroundImageUrl})`,
 		}"
 	>
 		<div class="back" :class="{ transparent }"></div>
@@ -91,6 +91,7 @@ import XSigninDialog from "@/components/MkSigninDialog.vue";
 import XSignupDialog from "@/components/MkSignupDialog.vue";
 import MkButton from "@/components/MkButton.vue";
 import { i18n } from "@/i18n";
+import { instance } from "@/instance";
 
 export default defineComponent({
 	components: {
@@ -128,6 +129,7 @@ export default defineComponent({
 				limit: 10,
 			},
 			i18n,
+			instance,
 		};
 	},
 
diff --git a/packages/client/src/widgets/aiscript.vue b/packages/client/src/widgets/aiscript.vue
index a00f143d85..7028c36d74 100644
--- a/packages/client/src/widgets/aiscript.vue
+++ b/packages/client/src/widgets/aiscript.vue
@@ -38,7 +38,7 @@ import type { GetFormResultType } from "@/scripts/form";
 import * as os from "@/os";
 import MkContainer from "@/components/MkContainer.vue";
 import { createAiScriptEnv } from "@/scripts/aiscript/api";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
 
@@ -84,7 +84,7 @@ const run = async () => {
 	const aiscript = new Interpreter(
 		createAiScriptEnv({
 			storageKey: "widget",
-			token: $i?.token,
+			token: me?.token,
 		}),
 		{
 			in: (q) => {
diff --git a/packages/client/src/widgets/button.vue b/packages/client/src/widgets/button.vue
index 5d2c8fa3d0..5d9482c3cd 100644
--- a/packages/client/src/widgets/button.vue
+++ b/packages/client/src/widgets/button.vue
@@ -18,7 +18,7 @@ import type {
 import { createAiScriptEnv } from "@/scripts/aiscript/api";
 import type { GetFormResultType } from "@/scripts/form";
 import * as os from "@/os";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 import MkButton from "@/components/MkButton.vue";
 
 const name = "button";
@@ -57,7 +57,7 @@ const run = async () => {
 	const aiscript = new Interpreter(
 		createAiScriptEnv({
 			storageKey: "widget",
-			token: $i?.token,
+			token: me?.token,
 		}),
 		{
 			in: (q) => {
diff --git a/packages/client/src/widgets/calendar.vue b/packages/client/src/widgets/calendar.vue
index b37970eb36..ef9af7ccb9 100644
--- a/packages/client/src/widgets/calendar.vue
+++ b/packages/client/src/widgets/calendar.vue
@@ -50,7 +50,7 @@ import { useWidgetPropsManager } from "./widget";
 import type { GetFormResultType } from "@/scripts/form";
 import { i18n } from "@/i18n";
 import { useInterval } from "@/scripts/use-interval";
-import { $i } from "@/reactiveAccount";
+import { me } from "@/me";
 
 const name = "calendar";
 
@@ -76,7 +76,7 @@ const { widgetProps, configure } = useWidgetPropsManager(
 	emit,
 );
 
-const hasBirthday = Boolean($i?.birthday);
+const hasBirthday = Boolean(me?.birthday);
 
 const year = ref(0);
 const month = ref(0);
@@ -123,7 +123,7 @@ const tick = () => {
 	isHoliday.value = now.getDay() === 0 || now.getDay() === 6;
 
 	if (hasBirthday) {
-		const [bdayYear, bdayMonth, bdayDay] = $i.birthday.split("-");
+		const [bdayYear, bdayMonth, bdayDay] = me.birthday.split("-");
 		if (month.value === +bdayMonth && day.value == +bdayDay) {
 			isBirthday.value = true;
 		}
diff --git a/packages/client/src/widgets/server-info.vue b/packages/client/src/widgets/server-info.vue
index 97dc6562c0..ffb42df847 100644
--- a/packages/client/src/widgets/server-info.vue
+++ b/packages/client/src/widgets/server-info.vue
@@ -3,14 +3,14 @@
 		<div
 			:class="$style.container"
 			:style="{
-				backgroundImage: `url(${$instance.bannerUrl})`,
+				backgroundImage: `url(${instance.bannerUrl})`,
 			}"
 		>
 			<div :class="$style.iconContainer">
 				<img
 					:src="
-						$instance.faviconUrl ||
-						$instance.iconUrl ||
+						instance.faviconUrl ||
+						instance.iconUrl ||
 						'/favicon.ico'
 					"
 					alt="Instance logo"
@@ -20,7 +20,7 @@
 			<div :class="$style.bodyContainer">
 				<div :class="$style.body">
 					<MkA :class="$style.name" to="/about" behavior="window">{{
-						$instance.name
+						instance.name
 					}}</MkA>
 					<div :class="$style.host">{{ host }}</div>
 				</div>
@@ -34,6 +34,7 @@ import type { Widget, WidgetComponentExpose } from "./widget";
 import { useWidgetPropsManager } from "./widget";
 import type { GetFormResultType } from "@/scripts/form";
 import { host } from "@/config";
+import { instance } from "@/instance";
 
 const name = "serverInfo";
 
diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json
index f9b6e6560e..8e955859f1 100644
--- a/packages/client/tsconfig.json
+++ b/packages/client/tsconfig.json
@@ -23,15 +23,10 @@
 		"useDefineForClassFields": true,
 		"baseUrl": ".",
 		"paths": {
-			"@/*": ["./src/*"],
+			"@/*": ["./src/*"]
 		},
-		"typeRoots": [
-			"node_modules/@types",
-			"@types",
-		],
-		"types": [
-			"vite/client",
-		],
+		"typeRoots": ["node_modules/@types", "@types"],
+		"types": ["vite/client"],
 		"lib": ["esnext", "dom"],
 		"jsx": "preserve"
 	},
diff --git a/packages/firefish-js/package.json b/packages/firefish-js/package.json
index 80b551f48d..bf06c167c9 100644
--- a/packages/firefish-js/package.json
+++ b/packages/firefish-js/package.json
@@ -22,10 +22,10 @@
 	},
 	"devDependencies": {
 		"@swc/cli": "0.3.10",
-		"@swc/core": "1.4.4",
+		"@swc/core": "1.4.8",
 		"@swc/types": "^0.1.5",
 		"@types/jest": "^29.5.12",
-		"@types/node": "20.11.24",
+		"@types/node": "20.11.28",
 		"jest": "^29.7.0",
 		"jest-fetch-mock": "^3.0.3",
 		"jest-websocket-mock": "^2.5.0",
@@ -33,7 +33,7 @@
 		"ts-jest": "^29.1.2",
 		"ts-node": "10.9.2",
 		"tsd": "^0.30.7",
-		"typescript": "5.3.3"
+		"typescript": "5.4.2"
 	},
 	"files": [
 		"built"
diff --git a/packages/firefish-js/src/api.ts b/packages/firefish-js/src/api.ts
index ea31fc6fb2..3a01fd6ae5 100644
--- a/packages/firefish-js/src/api.ts
+++ b/packages/firefish-js/src/api.ts
@@ -1,4 +1,4 @@
-import { Endpoints } from "./api.types";
+import type { Endpoints } from "./api.types";
 
 const MK_API_ERROR = Symbol();
 
diff --git a/packages/firefish-js/src/api.types.ts b/packages/firefish-js/src/api.types.ts
index 417f823267..5db210b932 100644
--- a/packages/firefish-js/src/api.types.ts
+++ b/packages/firefish-js/src/api.types.ts
@@ -1,4 +1,4 @@
-import {
+import type {
 	Ad,
 	Announcement,
 	Antenna,
@@ -17,7 +17,7 @@ import {
 	FollowingFollowerPopulated,
 	GalleryPost,
 	Instance,
-	InstanceMetadata,
+	// InstanceMetadata,
 	LiteInstanceMetadata,
 	MeDetailed,
 	MessagingMessage,
diff --git a/packages/firefish-js/src/index.ts b/packages/firefish-js/src/index.ts
index faee90a592..cb08fad8ab 100644
--- a/packages/firefish-js/src/index.ts
+++ b/packages/firefish-js/src/index.ts
@@ -11,7 +11,7 @@ export {
 	Connection as ChannelConnection,
 	StreamTypes,
 	acct,
-	Acct,
+	type Acct,
 };
 
 export const permissions = consts.permissions;
diff --git a/packages/firefish-js/src/streaming.ts b/packages/firefish-js/src/streaming.ts
index ae46e8a4ab..7944e45d0b 100644
--- a/packages/firefish-js/src/streaming.ts
+++ b/packages/firefish-js/src/streaming.ts
@@ -1,6 +1,6 @@
 import { EventEmitter } from "eventemitter3";
 import ReconnectingWebsocket from "reconnecting";
-import { BroadcastEvents, Channels } from "./streaming.types";
+import type { BroadcastEvents, Channels } from "./streaming.types";
 
 function autobind(instance: any): void {
 	const prototype = Object.getPrototypeOf(instance);
diff --git a/packages/sw/package.json b/packages/sw/package.json
index f6cf91384d..7de88fb077 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -11,7 +11,7 @@
 	"devDependencies": {
 		"firefish-js": "workspace:*",
 		"idb-keyval": "^6.2.1",
-		"vite": "5.1.5",
+		"vite": "5.1.6",
 		"vite-plugin-compression": "^0.5.1"
 	}
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0f7e1d67dd..39f4db0e07 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -25,23 +25,23 @@ importers:
         version: 4.1.0
     devDependencies:
       '@biomejs/biome':
-        specifier: 1.5.3
-        version: 1.5.3
+        specifier: 1.6.1
+        version: 1.6.1
       '@biomejs/cli-darwin-arm64':
-        specifier: ^1.5.3
-        version: 1.5.3
+        specifier: ^1.6.1
+        version: 1.6.1
       '@biomejs/cli-darwin-x64':
-        specifier: ^1.5.3
-        version: 1.5.3
+        specifier: ^1.6.1
+        version: 1.6.1
       '@biomejs/cli-linux-arm64':
-        specifier: ^1.5.3
-        version: 1.5.3
+        specifier: ^1.6.1
+        version: 1.6.1
       '@biomejs/cli-linux-x64':
-        specifier: ^1.5.3
-        version: 1.5.3
+        specifier: ^1.6.1
+        version: 1.6.1
       '@types/node':
-        specifier: 20.11.24
-        version: 20.11.24
+        specifier: 20.11.28
+        version: 20.11.28
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -49,20 +49,20 @@ importers:
         specifier: 8.15.4
         version: 8.15.4
       typescript:
-        specifier: 5.3.3
-        version: 5.3.3
+        specifier: 5.4.2
+        version: 5.4.2
 
   packages/backend:
     dependencies:
       '@bull-board/api':
-        specifier: 5.14.2
-        version: 5.14.2(@bull-board/ui@5.14.2)
+        specifier: 5.15.1
+        version: 5.15.1(@bull-board/ui@5.15.1)
       '@bull-board/koa':
-        specifier: 5.14.2
-        version: 5.14.2(@types/koa@2.15.0)(pug@3.0.2)
+        specifier: 5.15.1
+        version: 5.15.1(@types/koa@2.15.0)(pug@3.0.2)
       '@bull-board/ui':
-        specifier: 5.14.2
-        version: 5.14.2
+        specifier: 5.15.1
+        version: 5.15.1
       '@discordapp/twemoji':
         specifier: ^15.0.2
         version: 15.0.2
@@ -82,8 +82,8 @@ importers:
         specifier: 1.7.0
         version: 1.7.0
       '@redocly/openapi-core':
-        specifier: 1.10.3
-        version: 1.10.3
+        specifier: 1.10.4
+        version: 1.10.4
       '@sinonjs/fake-timers':
         specifier: 11.2.2
         version: 11.2.2
@@ -91,20 +91,20 @@ importers:
         specifier: ^15.0.0
         version: 15.0.0
       adm-zip:
-        specifier: ^0.5.10
-        version: 0.5.10
+        specifier: ^0.5.12
+        version: 0.5.12
       ajv:
         specifier: 8.12.0
         version: 8.12.0
       archiver:
-        specifier: 7.0.0
-        version: 7.0.0
+        specifier: 7.0.1
+        version: 7.0.1
       argon2:
         specifier: ^0.40.1
         version: 0.40.1
       aws-sdk:
-        specifier: 2.1571.0
-        version: 2.1571.0
+        specifier: 2.1578.0
+        version: 2.1578.0
       axios:
         specifier: ^1.6.7
         version: 1.6.7
@@ -142,8 +142,8 @@ importers:
         specifier: 0.5.4
         version: 0.5.4
       date-fns:
-        specifier: 3.3.1
-        version: 3.3.1
+        specifier: 3.5.0
+        version: 3.5.0
       decompress:
         specifier: ^4.2.1
         version: 4.2.1
@@ -169,14 +169,14 @@ importers:
         specifier: ^4.0.0
         version: 4.0.0
       got:
-        specifier: 14.2.0
-        version: 14.2.0
+        specifier: 14.2.1
+        version: 14.2.1
       gunzip-maybe:
         specifier: ^1.4.2
         version: 1.4.2
       happy-dom:
-        specifier: ^13.6.2
-        version: 13.6.2
+        specifier: ^13.8.6
+        version: 13.8.6
       hpagent:
         specifier: 1.2.0
         version: 1.2.0
@@ -202,8 +202,8 @@ importers:
         specifier: 11.1.0
         version: 11.1.0
       koa:
-        specifier: 2.15.0
-        version: 2.15.0
+        specifier: 2.15.1
+        version: 2.15.1
       koa-body:
         specifier: ^6.0.1
         version: 6.0.1
@@ -253,8 +253,8 @@ importers:
         specifier: 3.3.2
         version: 3.3.2
       nodemailer:
-        specifier: 6.9.11
-        version: 6.9.11
+        specifier: 6.9.12
+        version: 6.9.12
       opencc-js:
         specifier: ^1.0.5
         version: 1.0.5
@@ -289,8 +289,8 @@ importers:
         specifier: 1.5.3
         version: 1.5.3
       qs:
-        specifier: 6.11.2
-        version: 6.11.2
+        specifier: 6.12.0
+        version: 6.12.0
       random-seed:
         specifier: 0.3.0
         version: 0.3.0
@@ -334,8 +334,8 @@ importers:
         specifier: 1.0.0
         version: 1.0.0
       systeminformation:
-        specifier: 5.22.0
-        version: 5.22.0
+        specifier: 5.22.2
+        version: 5.22.2
       tar-stream:
         specifier: ^3.1.7
         version: 3.1.7
@@ -373,10 +373,10 @@ importers:
     devDependencies:
       '@swc/cli':
         specifier: 0.3.10
-        version: 0.3.10(@swc/core@1.4.4)
+        version: 0.3.10(@swc/core@1.4.8)
       '@swc/core':
-        specifier: 1.4.4
-        version: 1.4.4
+        specifier: 1.4.8
+        version: 1.4.8
       '@types/adm-zip':
         specifier: ^0.5.5
         version: 0.5.5
@@ -402,8 +402,8 @@ importers:
         specifier: 1.5.13
         version: 1.5.13
       '@types/jsrsasign':
-        specifier: 10.5.12
-        version: 10.5.12
+        specifier: 10.5.13
+        version: 10.5.13
       '@types/koa':
         specifier: 2.15.0
         version: 2.15.0
@@ -438,8 +438,8 @@ importers:
         specifier: 10.0.6
         version: 10.0.6
       '@types/node':
-        specifier: 20.11.24
-        version: 20.11.24
+        specifier: 20.11.28
+        version: 20.11.28
       '@types/node-fetch':
         specifier: 2.6.11
         version: 2.6.11
@@ -526,22 +526,22 @@ importers:
         version: 2.0.0
       swc-loader:
         specifier: ^0.2.6
-        version: 0.2.6(@swc/core@1.4.4)(webpack@5.90.3)
+        version: 0.2.6(@swc/core@1.4.8)(webpack@5.90.3)
       ts-loader:
         specifier: 9.5.1
-        version: 9.5.1(typescript@5.3.3)(webpack@5.90.3)
+        version: 9.5.1(typescript@5.4.2)(webpack@5.90.3)
       ts-node:
         specifier: 10.9.2
-        version: 10.9.2(@swc/core@1.4.4)(@types/node@20.11.24)(typescript@5.3.3)
+        version: 10.9.2(@swc/core@1.4.8)(@types/node@20.11.28)(typescript@5.4.2)
       tsconfig-paths:
         specifier: 4.2.0
         version: 4.2.0
       typescript:
-        specifier: 5.3.3
-        version: 5.3.3
+        specifier: 5.4.2
+        version: 5.4.2
       webpack:
         specifier: ^5.90.3
-        version: 5.90.3(@swc/core@1.4.4)
+        version: 5.90.3(@swc/core@1.4.8)
       ws:
         specifier: 8.16.0
         version: 8.16.0
@@ -559,22 +559,22 @@ importers:
     devDependencies:
       '@eslint-sets/eslint-config-vue3':
         specifier: ^5.12.0
-        version: 5.12.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3)
+        version: 5.12.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2)
       '@eslint-sets/eslint-config-vue3-ts':
         specifier: ^3.3.0
-        version: 3.3.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3)
+        version: 3.3.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2)
       '@phosphor-icons/web':
         specifier: ^2.0.3
         version: 2.0.3
       '@rollup/plugin-alias':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.12.0)
+        version: 5.1.0(rollup@4.13.0)
       '@rollup/plugin-json':
         specifier: 6.1.0
-        version: 6.1.0(rollup@4.12.0)
+        version: 6.1.0(rollup@4.13.0)
       '@rollup/pluginutils':
         specifier: ^5.1.0
-        version: 5.1.0(rollup@4.12.0)
+        version: 5.1.0(rollup@4.13.0)
       '@syuilo/aiscript':
         specifier: 0.17.0
         version: 0.17.0
@@ -622,7 +622,7 @@ importers:
         version: 9.0.8
       '@vitejs/plugin-vue':
         specifier: 5.0.4
-        version: 5.0.4(vite@5.1.5)(vue@3.4.21)
+        version: 5.0.4(vite@5.1.6)(vue@3.4.21)
       '@vue/runtime-core':
         specifier: 3.4.21
         version: 3.4.21
@@ -646,7 +646,7 @@ importers:
         version: 4.4.2
       chartjs-adapter-date-fns:
         specifier: 3.0.0
-        version: 3.0.0(chart.js@4.4.2)(date-fns@3.3.1)
+        version: 3.0.0(chart.js@4.4.2)(date-fns@3.5.0)
       chartjs-chart-matrix:
         specifier: ^2.0.1
         version: 2.0.1(chart.js@4.4.2)
@@ -666,8 +666,8 @@ importers:
         specifier: 2.0.0-beta.4
         version: 2.0.0-beta.4
       date-fns:
-        specifier: 3.3.1
-        version: 3.3.1
+        specifier: 3.5.0
+        version: 3.5.0
       emojilib:
         specifier: ^3.0.11
         version: 3.0.11
@@ -732,14 +732,14 @@ importers:
         specifier: 2.3.1
         version: 2.3.1
       rollup:
-        specifier: 4.12.0
-        version: 4.12.0
+        specifier: 4.13.0
+        version: 4.13.0
       s-age:
         specifier: 1.1.2
         version: 1.1.2
       sass:
-        specifier: 1.71.1
-        version: 1.71.1
+        specifier: 1.72.0
+        version: 1.72.0
       seedrandom:
         specifier: 3.0.5
         version: 3.0.5
@@ -768,8 +768,8 @@ importers:
         specifier: ^1.3.4
         version: 1.3.4
       typescript:
-        specifier: 5.3.3
-        version: 5.3.3
+        specifier: 5.4.2
+        version: 5.4.2
       unicode-emoji-json:
         specifier: ^0.4.0
         version: 0.4.0
@@ -777,14 +777,14 @@ importers:
         specifier: 9.0.1
         version: 9.0.1
       vite:
-        specifier: 5.1.5
-        version: 5.1.5(@types/node@20.11.24)(sass@1.71.1)
+        specifier: 5.1.6
+        version: 5.1.6(@types/node@20.11.28)(sass@1.72.0)
       vite-plugin-compression:
         specifier: ^0.5.1
-        version: 0.5.1(vite@5.1.5)
+        version: 0.5.1(vite@5.1.6)
       vue:
         specifier: 3.4.21
-        version: 3.4.21(typescript@5.3.3)
+        version: 3.4.21(typescript@5.4.2)
       vue-draggable-plus:
         specifier: ^0.3.5
         version: 0.3.5(@types/sortablejs@1.15.4)
@@ -810,10 +810,10 @@ importers:
     devDependencies:
       '@swc/cli':
         specifier: 0.3.10
-        version: 0.3.10(@swc/core@1.4.4)
+        version: 0.3.10(@swc/core@1.4.8)
       '@swc/core':
-        specifier: 1.4.4
-        version: 1.4.4
+        specifier: 1.4.8
+        version: 1.4.8
       '@swc/types':
         specifier: ^0.1.5
         version: 0.1.5
@@ -821,11 +821,11 @@ importers:
         specifier: ^29.5.12
         version: 29.5.12
       '@types/node':
-        specifier: 20.11.24
-        version: 20.11.24
+        specifier: 20.11.28
+        version: 20.11.28
       jest:
         specifier: ^29.7.0
-        version: 29.7.0(@types/node@20.11.24)(ts-node@10.9.2)
+        version: 29.7.0(@types/node@20.11.28)(ts-node@10.9.2)
       jest-fetch-mock:
         specifier: ^3.0.3
         version: 3.0.3
@@ -837,16 +837,16 @@ importers:
         version: 9.3.1
       ts-jest:
         specifier: ^29.1.2
-        version: 29.1.2(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.3.3)
+        version: 29.1.2(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.4.2)
       ts-node:
         specifier: 10.9.2
-        version: 10.9.2(@swc/core@1.4.4)(@types/node@20.11.24)(typescript@5.3.3)
+        version: 10.9.2(@swc/core@1.4.8)(@types/node@20.11.28)(typescript@5.4.2)
       tsd:
         specifier: ^0.30.7
         version: 0.30.7
       typescript:
-        specifier: 5.3.3
-        version: 5.3.3
+        specifier: 5.4.2
+        version: 5.4.2
 
   packages/megalodon:
     dependencies:
@@ -975,11 +975,11 @@ importers:
         specifier: ^6.2.1
         version: 6.2.1
       vite:
-        specifier: 5.1.5
-        version: 5.1.5(@types/node@20.11.24)(sass@1.71.1)
+        specifier: 5.1.6
+        version: 5.1.6(@types/node@20.11.28)(sass@1.72.0)
       vite-plugin-compression:
         specifier: ^0.5.1
-        version: 0.5.1(vite@5.1.5)
+        version: 0.5.1(vite@5.1.6)
 
 packages:
 
@@ -1539,38 +1539,38 @@ packages:
     resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
     dev: true
 
-  /@biomejs/biome@1.5.3:
-    resolution: {integrity: sha512-yvZCa/g3akwTaAQ7PCwPWDCkZs3Qa5ONg/fgOUT9e6wAWsPftCjLQFPXBeGxPK30yZSSpgEmRCfpGTmVbUjGgg==}
+  /@biomejs/biome@1.6.1:
+    resolution: {integrity: sha512-SILQvA2S0XeaOuu1bivv6fQmMo7zMfr2xqDEN+Sz78pGbAKZnGmg0emsXjQWoBY/RVm9kPCgX+aGEpZZTYaM7w==}
     engines: {node: '>=14.*'}
     hasBin: true
     requiresBuild: true
     optionalDependencies:
-      '@biomejs/cli-darwin-arm64': 1.5.3
-      '@biomejs/cli-darwin-x64': 1.5.3
-      '@biomejs/cli-linux-arm64': 1.5.3
-      '@biomejs/cli-linux-arm64-musl': 1.5.3
-      '@biomejs/cli-linux-x64': 1.5.3
-      '@biomejs/cli-linux-x64-musl': 1.5.3
-      '@biomejs/cli-win32-arm64': 1.5.3
-      '@biomejs/cli-win32-x64': 1.5.3
+      '@biomejs/cli-darwin-arm64': 1.6.1
+      '@biomejs/cli-darwin-x64': 1.6.1
+      '@biomejs/cli-linux-arm64': 1.6.1
+      '@biomejs/cli-linux-arm64-musl': 1.6.1
+      '@biomejs/cli-linux-x64': 1.6.1
+      '@biomejs/cli-linux-x64-musl': 1.6.1
+      '@biomejs/cli-win32-arm64': 1.6.1
+      '@biomejs/cli-win32-x64': 1.6.1
     dev: true
 
-  /@biomejs/cli-darwin-arm64@1.5.3:
-    resolution: {integrity: sha512-ImU7mh1HghEDyqNmxEZBoMPr8SxekkZuYcs+gynKlNW+TALQs7swkERiBLkG9NR0K1B3/2uVzlvYowXrmlW8hw==}
+  /@biomejs/cli-darwin-arm64@1.6.1:
+    resolution: {integrity: sha512-KlvY00iB9T/vFi4m/GXxEyYkYnYy6aw06uapzUIIdiMMj7I/pmZu7CsZlzWdekVD0j+SsQbxdZMsb0wPhnRSsg==}
     engines: {node: '>=14.*'}
     cpu: [arm64]
     os: [darwin]
     dev: true
 
-  /@biomejs/cli-darwin-x64@1.5.3:
-    resolution: {integrity: sha512-vCdASqYnlpq/swErH7FD6nrFz0czFtK4k/iLgj0/+VmZVjineFPgevOb+Sr9vz0tk0GfdQO60bSpI74zU8M9Dw==}
+  /@biomejs/cli-darwin-x64@1.6.1:
+    resolution: {integrity: sha512-jP4E8TXaQX5e3nvRJSzB+qicZrdIDCrjR0sSb1DaDTx4JPZH5WXq/BlTqAyWi3IijM+IYMjWqAAK4kOHsSCzxw==}
     engines: {node: '>=14.*'}
     cpu: [x64]
     os: [darwin]
     dev: true
 
-  /@biomejs/cli-linux-arm64-musl@1.5.3:
-    resolution: {integrity: sha512-DYuMizUYUBYfS0IHGjDrOP1RGipqWfMGEvNEJ398zdtmCKLXaUvTimiox5dvx4X15mBK5M2m8wgWUgOP1giUpQ==}
+  /@biomejs/cli-linux-arm64-musl@1.6.1:
+    resolution: {integrity: sha512-YdkDgFecdHJg7PJxAMaZIixVWGB6St4yH08BHagO0fEhNNiY8cAKEVo2mcXlsnEiTMpeSEAY9VxLUrVT3IVxpw==}
     engines: {node: '>=14.*'}
     cpu: [arm64]
     os: [linux]
@@ -1578,15 +1578,15 @@ packages:
     dev: true
     optional: true
 
-  /@biomejs/cli-linux-arm64@1.5.3:
-    resolution: {integrity: sha512-cupBQv0sNF1OKqBfx7EDWMSsKwRrBUZfjXawT4s6hKV6ALq7p0QzWlxr/sDmbKMLOaLQtw2Qgu/77N9rm+f9Rg==}
+  /@biomejs/cli-linux-arm64@1.6.1:
+    resolution: {integrity: sha512-nxD1UyX3bWSl/RSKlib/JsOmt+652/9yieogdSC/UTLgVCZYOF7u8L/LK7kAa0Y4nA8zSPavAQTgko7mHC2ObA==}
     engines: {node: '>=14.*'}
     cpu: [arm64]
     os: [linux]
     dev: true
 
-  /@biomejs/cli-linux-x64-musl@1.5.3:
-    resolution: {integrity: sha512-UUHiAnlDqr2Y/LpvshBFhUYMWkl2/Jn+bi3U6jKuav0qWbbBKU/ByHgR4+NBxpKBYoCtWxhnmatfH1bpPIuZMw==}
+  /@biomejs/cli-linux-x64-musl@1.6.1:
+    resolution: {integrity: sha512-aSISIDmxq04NNy7tm4x9rBk2vH0ub2VDIE4outEmdC2LBtEJoINiphlZagx/FvjbsqUfygent9QUSn0oREnAXg==}
     engines: {node: '>=14.*'}
     cpu: [x64]
     os: [linux]
@@ -1594,15 +1594,15 @@ packages:
     dev: true
     optional: true
 
-  /@biomejs/cli-linux-x64@1.5.3:
-    resolution: {integrity: sha512-YQrSArQvcv4FYsk7Q91Yv4uuu5F8hJyORVcv3zsjCLGkjIjx2RhjYLpTL733SNL7v33GmOlZY0eFR1ko38tuUw==}
+  /@biomejs/cli-linux-x64@1.6.1:
+    resolution: {integrity: sha512-BYAzenlMF3QdngjNFw9QVBXKGNzeecqwF3pwDgUGEvU7OJpn1/lyVkJVxYPtVGRNdjQ9e6l/s8NjKuBpW/ZR4Q==}
     engines: {node: '>=14.*'}
     cpu: [x64]
     os: [linux]
     dev: true
 
-  /@biomejs/cli-win32-arm64@1.5.3:
-    resolution: {integrity: sha512-HxatYH7vf/kX9nrD+pDYuV2GI9GV8EFo6cfKkahAecTuZLPxryHx1WEfJthp5eNsE0+09STGkKIKjirP0ufaZA==}
+  /@biomejs/cli-win32-arm64@1.6.1:
+    resolution: {integrity: sha512-/eCHQKZ1kEawUpkSuXq4urtxMsD1P1678OPG3zNKt3ru16AqqspLdO3jzBe3k74xCPYnQ36e9Yqc97Mo0qgPtg==}
     engines: {node: '>=14.*'}
     cpu: [arm64]
     os: [win32]
@@ -1610,8 +1610,8 @@ packages:
     dev: true
     optional: true
 
-  /@biomejs/cli-win32-x64@1.5.3:
-    resolution: {integrity: sha512-fMvbSouZEASU7mZH8SIJSANDm5OqsjgtVXlbUqxwed6BP7uuHRSs396Aqwh2+VoW8fwTpp6ybIUoC9FrzB0kyA==}
+  /@biomejs/cli-win32-x64@1.6.1:
+    resolution: {integrity: sha512-5TUZbzBwnDLFxLVGEPsorNi6eC2Gt+z4Oei9Qvq0M/4c4/mjZ96ABgwao/tMxf4ZBr/qyy2YdvF+gX9Rc+xC0A==}
     engines: {node: '>=14.*'}
     cpu: [x64]
     os: [win32]
@@ -1619,22 +1619,22 @@ packages:
     dev: true
     optional: true
 
-  /@bull-board/api@5.14.2(@bull-board/ui@5.14.2):
-    resolution: {integrity: sha512-0wppAGPU7ZMwWMpzkmtrlmm7ySI5immymyaRS1cVNJ54rUiGOZP5tnm+Sj7MwPdf63rxqIM843un8+PvQyARGg==}
+  /@bull-board/api@5.15.1(@bull-board/ui@5.15.1):
+    resolution: {integrity: sha512-yJqv2uhMTC5BE2SwK/zG7kTloZyOFOujzPtP09OF9S8GvTalCwSTk7EsLQOUMHTUhyiVZ8JpUyJz8oytZ77+xQ==}
     peerDependencies:
-      '@bull-board/ui': 5.14.2
+      '@bull-board/ui': 5.15.1
     dependencies:
-      '@bull-board/ui': 5.14.2
+      '@bull-board/ui': 5.15.1
       redis-info: 3.1.0
     dev: false
 
-  /@bull-board/koa@5.14.2(@types/koa@2.15.0)(pug@3.0.2):
-    resolution: {integrity: sha512-SlCwZhFLS3c8MS6wCeq6rPF9cgxJ2a8lhR+EyujdgJMjQGVUOMQzJOOkBjQF2fbS2ixC923/6NOYMNwq87dhfA==}
+  /@bull-board/koa@5.15.1(@types/koa@2.15.0)(pug@3.0.2):
+    resolution: {integrity: sha512-O6Mq0qoVK125YHXfNYUY7g3QrhvPcU0Vg81TC7mEbt+rrdPJa7twvP0VAqd/Ga3SiDS+cth+9S2bnPQQ3CoOoA==}
     dependencies:
-      '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2)
-      '@bull-board/ui': 5.14.2
+      '@bull-board/api': 5.15.1(@bull-board/ui@5.15.1)
+      '@bull-board/ui': 5.15.1
       ejs: 3.1.9
-      koa: 2.15.0
+      koa: 2.15.1
       koa-mount: 4.0.0
       koa-router: 10.1.1
       koa-static: 5.0.0
@@ -1696,10 +1696,10 @@ packages:
       - whiskers
     dev: false
 
-  /@bull-board/ui@5.14.2:
-    resolution: {integrity: sha512-NiyKWLjKjy29I6ySCnSYbzGX4ZJyPE4xlS5/Z5dVsF2bJLoAV+yD1obflxteJMt60FiEgLV7tfs6tMSVa+Htew==}
+  /@bull-board/ui@5.15.1:
+    resolution: {integrity: sha512-+jpHdBTaqC2D9QDlix9j0EeqiJgN+RB8aH/A7J0VwVMLnbM820ZFPbrlXIN3Ph0hX2hgc+aHabZjs4Rjmwpqng==}
     dependencies:
-      '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2)
+      '@bull-board/api': 5.15.1(@bull-board/ui@5.15.1)
     dev: false
 
   /@cbor-extract/cbor-extract-darwin-arm64@2.2.0:
@@ -2145,7 +2145,7 @@ packages:
       - supports-color
     dev: true
 
-  /@eslint-sets/eslint-config-basic@5.12.0(@babel/core@7.23.2)(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3):
+  /@eslint-sets/eslint-config-basic@5.12.0(@babel/core@7.23.2)(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2):
     resolution: {integrity: sha512-AgECfmJsiVOWKmvgjv780VuuoT9SE6PRgxGTtytHSfE9b9MAJjHxToVTKtD4UEKvocEGbg2EcwqGbff8cxDWKw==}
     peerDependencies:
       eslint: '>=7.4.0'
@@ -2171,7 +2171,7 @@ packages:
       eslint-plugin-yml: 1.12.2(eslint@8.57.0)
       jsonc-eslint-parser: 2.4.0
       prettier: 3.2.5
-      typescript: 5.3.3
+      typescript: 5.4.2
       vue-eslint-parser: 9.4.2(eslint@8.57.0)
       yaml-eslint-parser: 1.2.2
     transitivePeerDependencies:
@@ -2183,7 +2183,7 @@ packages:
       - supports-color
     dev: true
 
-  /@eslint-sets/eslint-config-ts@3.3.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3):
+  /@eslint-sets/eslint-config-ts@3.3.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2):
     resolution: {integrity: sha512-4Vj3KxYx16hmW6AyEv1mil0gVN8H3rdJt8TRWufbAj0ZN+EjwOPf3TqE7ASCYto/NpA8xWQY3NGm/og9Or/dDQ==}
     peerDependencies:
       eslint: '>=8.0.0'
@@ -2191,14 +2191,14 @@ packages:
       typescript: '>=4.0.0'
     dependencies:
       '@eslint-sets/eslint-config-basic': 3.3.0(@babel/core@7.23.2)(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(prettier@3.2.5)
-      '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.2)
+      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.2)
       eslint: 8.57.0
       eslint-config-prettier: 8.9.0(eslint@8.57.0)
       eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.9.0)(eslint@8.57.0)(prettier@3.2.5)
       eslint-plugin-tsdoc: 0.2.17
       prettier: 3.2.5
-      typescript: 5.3.3
+      typescript: 5.4.2
     transitivePeerDependencies:
       - '@babel/core'
       - eslint-import-resolver-typescript
@@ -2206,7 +2206,7 @@ packages:
       - supports-color
     dev: true
 
-  /@eslint-sets/eslint-config-ts@5.12.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3):
+  /@eslint-sets/eslint-config-ts@5.12.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2):
     resolution: {integrity: sha512-7vOzV6qYv0SbA9W17m9lkG/Zv+qVeCcAbWEY1d9hUbBHx9Ip48kNMNVDrnh97zUORXGcmjxsZ81W2lC36Ox2pw==}
     peerDependencies:
       eslint: '>=7.4.0'
@@ -2216,15 +2216,15 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@eslint-sets/eslint-config-basic': 5.12.0(@babel/core@7.23.2)(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3)
-      '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
+      '@eslint-sets/eslint-config-basic': 5.12.0(@babel/core@7.23.2)(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2)
+      '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.2)
+      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.2)
       eslint: 8.57.0
       eslint-config-prettier: 9.1.0(eslint@8.57.0)
       eslint-plugin-prettier: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5)
       eslint-plugin-tsdoc: 0.2.17
       prettier: 3.2.5
-      typescript: 5.3.3
+      typescript: 5.4.2
     transitivePeerDependencies:
       - '@babel/core'
       - '@types/eslint'
@@ -2233,16 +2233,16 @@ packages:
       - supports-color
     dev: true
 
-  /@eslint-sets/eslint-config-vue3-ts@3.3.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3):
+  /@eslint-sets/eslint-config-vue3-ts@3.3.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2):
     resolution: {integrity: sha512-KX3VFuS5U4FYKfZ6PABQjl54BMpNapNjYYe103Nm2Zy8y9zphDCBAARbhU97XNSvzkurve7HhJcsi9gXrWlGFA==}
     peerDependencies:
       eslint: '>=8.0.0'
       prettier: '>=2.0.0'
       typescript: '>=4.0.0'
     dependencies:
-      '@eslint-sets/eslint-config-ts': 3.3.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3)
-      '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.3.3)
+      '@eslint-sets/eslint-config-ts': 3.3.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2)
+      '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.2)
+      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.2)
       eslint: 8.57.0
       eslint-config-prettier: 8.9.0(eslint@8.57.0)
       eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.9.0)(eslint@8.57.0)(prettier@3.2.5)
@@ -2251,7 +2251,7 @@ packages:
       eslint-plugin-vue: 9.16.1(eslint@8.57.0)
       eslint-plugin-vue-scoped-css: 2.5.0(eslint@8.57.0)(vue-eslint-parser@9.3.1)
       prettier: 3.2.5
-      typescript: 5.3.3
+      typescript: 5.4.2
       vue-eslint-parser: 9.3.1(eslint@8.57.0)
     transitivePeerDependencies:
       - '@babel/core'
@@ -2260,7 +2260,7 @@ packages:
       - supports-color
     dev: true
 
-  /@eslint-sets/eslint-config-vue3@5.12.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3):
+  /@eslint-sets/eslint-config-vue3@5.12.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2):
     resolution: {integrity: sha512-gQBmQicZihPcxncIdkKagQGZ2dH+97ioAlUpsaczEdgY9pLrLOU5oGTetjbaxAp6zGS2sXm1n0i2BnwRIlt4Bg==}
     peerDependencies:
       eslint: '>=7.4.0'
@@ -2270,10 +2270,10 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@eslint-sets/eslint-config-basic': 5.12.0(@babel/core@7.23.2)(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3)
-      '@eslint-sets/eslint-config-ts': 5.12.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.3.3)
-      '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
+      '@eslint-sets/eslint-config-basic': 5.12.0(@babel/core@7.23.2)(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2)
+      '@eslint-sets/eslint-config-ts': 5.12.0(@babel/core@7.23.2)(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.2)
+      '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.2)
+      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.2)
       eslint: 8.57.0
       eslint-config-prettier: 9.1.0(eslint@8.57.0)
       eslint-plugin-jsdoc: 48.0.6(eslint@8.57.0)
@@ -2284,7 +2284,7 @@ packages:
       eslint-plugin-vue-scoped-css: 2.7.2(eslint@8.57.0)(vue-eslint-parser@9.4.2)
       local-pkg: 0.5.0
       prettier: 3.2.5
-      typescript: 5.3.3
+      typescript: 5.4.2
       vue-eslint-parser: 9.4.2(eslint@8.57.0)
     transitivePeerDependencies:
       - '@babel/core'
@@ -2598,7 +2598,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -2619,14 +2619,14 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       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.24)(ts-node@10.9.2)
+      jest-config: 29.7.0(@types/node@20.11.28)(ts-node@10.9.2)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -2654,7 +2654,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       jest-mock: 29.7.0
     dev: true
 
@@ -2681,7 +2681,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -2714,7 +2714,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.20
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       chalk: 4.1.2
       collect-v8-coverage: 1.0.2
       exit: 0.1.2
@@ -2802,7 +2802,7 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.5
       '@types/istanbul-reports': 3.0.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/yargs': 17.0.29
       chalk: 4.1.2
     dev: true
@@ -3298,11 +3298,16 @@ packages:
       uri-js: 4.4.1
     dev: false
 
-  /@redocly/openapi-core@1.10.3:
-    resolution: {integrity: sha512-4SnIWh8r3EM1ylcoHIJSnQnuvqRTpQMnf2RU3BfVdcCBa0A1uEyH6XSxgcO5ehxfQGuGGpUXJ+vPh32PUaQDkA==}
+  /@redocly/config@0.1.3:
+    resolution: {integrity: sha512-DjgGwhyolxDLO7hP1V8h6qQUUTkqN7P/xG2OMgsABJ1Pr40GG32+cqx9oBbKX9iN+JXh6kVUZgBMcPPB1fbyLA==}
+    dev: false
+
+  /@redocly/openapi-core@1.10.4:
+    resolution: {integrity: sha512-GzvAuoVtHk75q/HqaRNqRUTZWYKpZ16HCOWIrx2txAvZrMoWCUNRVsELY91W/ilH/Cepj6t/Nh+bpJ7o/mcN/g==}
     engines: {node: '>=14.19.0', npm: '>=7.0.0'}
     dependencies:
       '@redocly/ajv': 8.11.0
+      '@redocly/config': 0.1.3
       colorette: 1.4.0
       js-levenshtein: 1.1.6
       js-yaml: 4.1.0
@@ -3315,7 +3320,7 @@ packages:
       - encoding
     dev: false
 
-  /@rollup/plugin-alias@5.1.0(rollup@4.12.0):
+  /@rollup/plugin-alias@5.1.0(rollup@4.13.0):
     resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -3324,11 +3329,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      rollup: 4.12.0
+      rollup: 4.13.0
       slash: 4.0.0
     dev: true
 
-  /@rollup/plugin-json@6.1.0(rollup@4.12.0):
+  /@rollup/plugin-json@6.1.0(rollup@4.13.0):
     resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -3337,8 +3342,8 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.12.0)
-      rollup: 4.12.0
+      '@rollup/pluginutils': 5.1.0(rollup@4.13.0)
+      rollup: 4.13.0
     dev: true
 
   /@rollup/pluginutils@4.2.1:
@@ -3349,7 +3354,7 @@ packages:
       picomatch: 2.3.1
     dev: true
 
-  /@rollup/pluginutils@5.1.0(rollup@4.12.0):
+  /@rollup/pluginutils@5.1.0(rollup@4.13.0):
     resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -3361,107 +3366,107 @@ packages:
       '@types/estree': 1.0.3
       estree-walker: 2.0.2
       picomatch: 2.3.1
-      rollup: 4.12.0
+      rollup: 4.13.0
     dev: true
 
-  /@rollup/rollup-android-arm-eabi@4.12.0:
-    resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==}
+  /@rollup/rollup-android-arm-eabi@4.13.0:
+    resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==}
     cpu: [arm]
     os: [android]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-android-arm64@4.12.0:
-    resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==}
+  /@rollup/rollup-android-arm64@4.13.0:
+    resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-darwin-arm64@4.12.0:
-    resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==}
+  /@rollup/rollup-darwin-arm64@4.13.0:
+    resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-darwin-x64@4.12.0:
-    resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==}
+  /@rollup/rollup-darwin-x64@4.13.0:
+    resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf@4.12.0:
-    resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==}
+  /@rollup/rollup-linux-arm-gnueabihf@4.13.0:
+    resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu@4.12.0:
-    resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
+  /@rollup/rollup-linux-arm64-gnu@4.13.0:
+    resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl@4.12.0:
-    resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
+  /@rollup/rollup-linux-arm64-musl@4.13.0:
+    resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-riscv64-gnu@4.12.0:
-    resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
+  /@rollup/rollup-linux-riscv64-gnu@4.13.0:
+    resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==}
     cpu: [riscv64]
     os: [linux]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu@4.12.0:
-    resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
+  /@rollup/rollup-linux-x64-gnu@4.13.0:
+    resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-x64-musl@4.12.0:
-    resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
+  /@rollup/rollup-linux-x64-musl@4.13.0:
+    resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc@4.12.0:
-    resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
+  /@rollup/rollup-win32-arm64-msvc@4.13.0:
+    resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc@4.12.0:
-    resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==}
+  /@rollup/rollup-win32-ia32-msvc@4.13.0:
+    resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     dev: true
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc@4.12.0:
-    resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==}
+  /@rollup/rollup-win32-x64-msvc@4.13.0:
+    resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
@@ -3507,7 +3512,7 @@ packages:
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
     dev: false
 
-  /@swc/cli@0.3.10(@swc/core@1.4.4):
+  /@swc/cli@0.3.10(@swc/core@1.4.8):
     resolution: {integrity: sha512-YWfYo9kXdbmIuGwIPth9geKgb0KssCMTdZa44zAN5KoqcuCP2rTW9s60heQDSRNpbtCmUr7BKF1VivsoHXrvrQ==}
     engines: {node: '>= 16.14.0'}
     hasBin: true
@@ -3519,7 +3524,7 @@ packages:
         optional: true
     dependencies:
       '@mole-inc/bin-wrapper': 8.0.1
-      '@swc/core': 1.4.4
+      '@swc/core': 1.4.8
       '@swc/counter': 0.1.3
       commander: 8.3.0
       fast-glob: 3.3.2
@@ -3541,88 +3546,88 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-darwin-arm64@1.4.4:
-    resolution: {integrity: sha512-goSHS8yvDgha93RHIV2Vn50neYasqbc4K1g/nKOV6T8kiKVv4w/rmqNJu9Aa0mPGVJtjcr0NvX6bBwE0T4HIzg==}
+  /@swc/core-darwin-arm64@1.4.8:
+    resolution: {integrity: sha512-hhQCffRTgzpTIbngSnC30vV6IJVTI9FFBF954WEsshsecVoCGFiMwazBbrkLG+RwXENTrMhgeREEFh6R3KRgKQ==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@swc/core-darwin-x64@1.4.4:
-    resolution: {integrity: sha512-PLfgL355qsl5c5kUPsFGITgVXoaqjp9sCd0Y5Z5uN7RtSOvwIX28e23eCxj02dOr7OBr8sq6qBlEMDV03T24Iw==}
+  /@swc/core-darwin-x64@1.4.8:
+    resolution: {integrity: sha512-P3ZBw8Jr8rKhY/J8d+6WqWriqngGTgHwtFeJ8MIakQJTbdYbFgXSZxcvDiERg3psbGeFXaUaPI0GO6BXv9k/OQ==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@swc/core-linux-arm-gnueabihf@1.4.4:
-    resolution: {integrity: sha512-BVEZVOGnaZvEcHm//KyYzhte46vdF67wLVtmQEXPAlrkRgZ3b/JSySeLXqeocAcOANWb1/SPHlEmPK5azP+JvQ==}
+  /@swc/core-linux-arm-gnueabihf@1.4.8:
+    resolution: {integrity: sha512-PP9JIJt19bUWhAGcQW6qMwTjZOcMyzkvZa0/LWSlDm0ORYVLmDXUoeQbGD3e0Zju9UiZxyulnpjEN0ZihJgPTA==}
     engines: {node: '>=10'}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@swc/core-linux-arm64-gnu@1.4.4:
-    resolution: {integrity: sha512-ZbOJfVbCjVMKdfvvJDOTpa3tGqU6tfxng1CDjA62RUcqa7sRbovrjSiw6mq5/4EoOF4zK8CtPIG+TlxKPapnuw==}
+  /@swc/core-linux-arm64-gnu@1.4.8:
+    resolution: {integrity: sha512-HvEWnwKHkoVUr5iftWirTApFJ13hGzhAY2CMw4lz9lur2m+zhPviRRED0FCI6T95Knpv7+8eUOr98Z7ctrG6DQ==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@swc/core-linux-arm64-musl@1.4.4:
-    resolution: {integrity: sha512-+Gjo1W4tY/4kgEe5h22iuCWkpKcPMccXwYaSLNvgBCBQADB0zKFfF0lNf7y6U+81NFEjhRsdwXMsRGZtgTpUrg==}
+  /@swc/core-linux-arm64-musl@1.4.8:
+    resolution: {integrity: sha512-kY8+qa7k/dEeBq9p0Hrta18QnJPpsiJvDQSLNaTIFpdM3aEM9zbkshWz8gaX5VVGUEALowCBUWqmzO4VaqM+2w==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@swc/core-linux-x64-gnu@1.4.4:
-    resolution: {integrity: sha512-PR/VbGm0LEkhpm5qClovZWhE/jYoQSyIeyPh8XY39uUI1u2yEfuz5UCW2sJJIWOvNiAfu7+TjW+9H/I7zBBDJA==}
+  /@swc/core-linux-x64-gnu@1.4.8:
+    resolution: {integrity: sha512-0WWyIw432wpO/zeGblwq4f2YWam4pn8Z/Ig4KzHMgthR/KmiLU3f0Z7eo45eVmq5vcU7Os1zi/Zb65OOt09q/w==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@swc/core-linux-x64-musl@1.4.4:
-    resolution: {integrity: sha512-poT9zub4CyVcH1cxwGdrGiZD3urfOaYs/Kd52ve3ymPPeQZq7qQwKqAB/9NxoSiJDaSzJv5OwTEfgaBYCyw0iw==}
+  /@swc/core-linux-x64-musl@1.4.8:
+    resolution: {integrity: sha512-p4yxvVS05rBNCrBaSTa20KK88vOwtg8ifTW7ec/yoab0bD5EwzzB8KbDmLLxE6uziFa0sdjF0dfRDwSZPex37Q==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@swc/core-win32-arm64-msvc@1.4.4:
-    resolution: {integrity: sha512-29V5/fBd6XXFb7J/ri9ZeSS/GTqXfSWa3BiE0zTNbASpQbEXf+YPYiAtO6c1HqNyQobKB9ni+w7sa8KkAGhHXw==}
+  /@swc/core-win32-arm64-msvc@1.4.8:
+    resolution: {integrity: sha512-jKuXihxAaqUnbFfvPxtmxjdJfs87F1GdBf33il+VUmSyWCP4BE6vW+/ReDAe8sRNsKyrZ3UH1vI5q1n64csBUA==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@swc/core-win32-ia32-msvc@1.4.4:
-    resolution: {integrity: sha512-2lKEGEjpBOq0z4Nk0tFP9wxVwxgz7FonmjCkzJ95GBb5KNvMrgQQrGNGX6L0hoBo/a1kE752I6V5pOaMyIq5xQ==}
+  /@swc/core-win32-ia32-msvc@1.4.8:
+    resolution: {integrity: sha512-O0wT4AGHrX8aBeH6c2ADMHgagAJc5Kf6W48U5moyYDAkkVnKvtSc4kGhjWhe1Yl0sI0cpYh2In2FxvYsb44eWw==}
     engines: {node: '>=10'}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@swc/core-win32-x64-msvc@1.4.4:
-    resolution: {integrity: sha512-xuN0oJhAewga8jNJkT5Wx25RPVnIEMZCYf4irqA5jiK6GckOdcXB8jvEJhggOxnJSW8RDsAtY5q+zw5kNkU+eA==}
+  /@swc/core-win32-x64-msvc@1.4.8:
+    resolution: {integrity: sha512-C2AYc3A2o+ECciqsJWRgIpp83Vk5EaRzHe7ed/xOWzVd0MsWR+fweEsyOjlmzHfpUxJSi46Ak3/BIZJlhZbXbg==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@swc/core@1.4.4:
-    resolution: {integrity: sha512-P88AHGWM8xPY3Tjj5360V6vqKCS5UfsyffPJVnr7BKSr45rlG4/pjEGGmFYQjg6ztgPyrGLYz1jSyzajTqTVIA==}
+  /@swc/core@1.4.8:
+    resolution: {integrity: sha512-uY2RSJcFPgNOEg12RQZL197LZX+MunGiKxsbxmh22VfVxrOYGRvh4mPANFlrD1yb38CgmW1wI6YgIi8LkIwmWg==}
     engines: {node: '>=10'}
     requiresBuild: true
     peerDependencies:
@@ -3634,16 +3639,16 @@ packages:
       '@swc/counter': 0.1.3
       '@swc/types': 0.1.5
     optionalDependencies:
-      '@swc/core-darwin-arm64': 1.4.4
-      '@swc/core-darwin-x64': 1.4.4
-      '@swc/core-linux-arm-gnueabihf': 1.4.4
-      '@swc/core-linux-arm64-gnu': 1.4.4
-      '@swc/core-linux-arm64-musl': 1.4.4
-      '@swc/core-linux-x64-gnu': 1.4.4
-      '@swc/core-linux-x64-musl': 1.4.4
-      '@swc/core-win32-arm64-msvc': 1.4.4
-      '@swc/core-win32-ia32-msvc': 1.4.4
-      '@swc/core-win32-x64-msvc': 1.4.4
+      '@swc/core-darwin-arm64': 1.4.8
+      '@swc/core-darwin-x64': 1.4.8
+      '@swc/core-linux-arm-gnueabihf': 1.4.8
+      '@swc/core-linux-arm64-gnu': 1.4.8
+      '@swc/core-linux-arm64-musl': 1.4.8
+      '@swc/core-linux-x64-gnu': 1.4.8
+      '@swc/core-linux-x64-musl': 1.4.8
+      '@swc/core-win32-arm64-msvc': 1.4.8
+      '@swc/core-win32-ia32-msvc': 1.4.8
+      '@swc/core-win32-x64-msvc': 1.4.8
 
   /@swc/counter@0.1.3:
     resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
@@ -3704,12 +3709,12 @@ packages:
   /@types/accepts@1.3.5:
     resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/adm-zip@0.5.5:
     resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/async-lock@1.4.0:
@@ -3786,20 +3791,20 @@ packages:
     resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/cacheable-request@6.0.3:
     resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
     dependencies:
-      '@types/http-cache-semantics': 4.0.1
+      '@types/http-cache-semantics': 4.0.4
       '@types/keyv': 3.1.4
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/responselike': 1.0.0
 
   /@types/co-body@6.1.0:
     resolution: {integrity: sha512-3e0q2jyDAnx/DSZi0z2H0yoZ2wt5yRDZ+P7ymcMObvq0ufWRT4tsajyO+Q1VwVWiv9PRR4W3YEjEzBjeZlhF+w==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/qs': 6.9.12
     dev: false
 
@@ -3816,7 +3821,7 @@ packages:
   /@types/connect@3.4.35:
     resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/content-disposition@0.5.8:
     resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==}
@@ -3827,7 +3832,7 @@ packages:
       '@types/connect': 3.4.35
       '@types/express': 4.17.17
       '@types/keygrip': 1.0.2
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/core-js@2.5.7:
     resolution: {integrity: sha512-EhO4Lcd2Rs2bZvQwIDMZ1qsaZk8DpdOkQCbKpK0vt7fSjJGXrCA7EPauR/BZ7eJXks1een4FX7JtlhS136fklA==}
@@ -3876,7 +3881,7 @@ packages:
   /@types/express-serve-static-core@4.17.35:
     resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/qs': 6.9.12
       '@types/range-parser': 1.2.4
       '@types/send': 0.17.1
@@ -3892,7 +3897,7 @@ packages:
   /@types/fluent-ffmpeg@2.1.24:
     resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/form-data@2.5.0:
@@ -3905,13 +3910,13 @@ packages:
   /@types/formidable@2.0.6:
     resolution: {integrity: sha512-L4HcrA05IgQyNYJj6kItuIkXrInJvsXTPC5B1i64FggWKKqSL+4hgt7asiSNva75AoLQjq29oPxFfU4GAQ6Z2w==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: false
 
   /@types/glob-stream@8.0.0:
     resolution: {integrity: sha512-fxTWwdQmX9LWSHD7ZLlv3BHR992mKcVcDnT/2v+l/QZZo7TfDdyasqlSYVzOnMGWhRbrWeWkbj/mgezFjKynhw==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/picomatch': 2.3.0
       '@types/streamx': 2.9.1
     dev: true
@@ -3920,26 +3925,26 @@ packages:
     resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/graceful-fs@4.1.8:
     resolution: {integrity: sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/gulp-rename@2.0.6:
     resolution: {integrity: sha512-pvZdJ004TpC4Ohk9l0CxEXzS9E0L72b5n6lkIEIaWUIy/RlqnkDMHVtEC6InDjd4rt0jZKcvTrDKxeT96WUYnw==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/vinyl': 2.0.7
     dev: true
 
   /@types/gulp@4.0.17:
     resolution: {integrity: sha512-+pKQynu2C/HS16kgmDlAicjtFYP8kaa86eE9P0Ae7GB5W29we/E2TIdbOWtEZD5XkpY+jr8fyqfwO6SWZecLpQ==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/undertaker': 1.2.8
       '@types/vinyl-fs': 3.0.2
       chokidar: 3.5.3
@@ -3948,12 +3953,8 @@ packages:
   /@types/http-assert@1.5.3:
     resolution: {integrity: sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==}
 
-  /@types/http-cache-semantics@4.0.1:
-    resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==}
-
   /@types/http-cache-semantics@4.0.4:
     resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
-    dev: false
 
   /@types/http-errors@2.0.1:
     resolution: {integrity: sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==}
@@ -4012,8 +4013,8 @@ packages:
     resolution: {integrity: sha512-n7fUU6W4kSYK8VQlf/LsE9kddBHPKhODoVOjsZswmve+2qLwBy6naWxs/EiuSZN9NU0N06Ra01FR+j87C62T0A==}
     dev: true
 
-  /@types/jsrsasign@10.5.12:
-    resolution: {integrity: sha512-sOA+eVnHU+FziThpMhuqs/tjFKe5gHVJKIS7g1BzhXP+e2FS8OvtzM0K3IzFxVksDOr98Gz5FJiZVxZ9uFoHhw==}
+  /@types/jsrsasign@10.5.13:
+    resolution: {integrity: sha512-vvVHLrXxoUZgBWTcJnTMSC4FAQcG2loK7N1Uy20I3nr/aUhetbGdfuwSzXkrMoll2RoYKW0IcMIN0I0bwMwVMQ==}
     dev: true
 
   /@types/katex@0.16.7:
@@ -4026,7 +4027,7 @@ packages:
   /@types/keyv@3.1.4:
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/koa-bodyparser@4.3.12:
     resolution: {integrity: sha512-hKMmRMVP889gPIdLZmmtou/BijaU1tHPyMNmcK7FAHAdATnRcGQQy78EqTTxLH1D4FTsrxIzklAQCso9oGoebQ==}
@@ -4079,7 +4080,7 @@ packages:
       '@types/http-errors': 2.0.1
       '@types/keygrip': 1.0.2
       '@types/koa-compose': 3.2.5
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/koa__cors@5.0.0:
     resolution: {integrity: sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g==}
@@ -4130,13 +4131,13 @@ packages:
   /@types/needle@3.2.0:
     resolution: {integrity: sha512-6XzvzEyJ2ozFNfPajFmqH9JOt0Hp+9TawaYpJT59iIP/zR0U37cfWCRwosyIeEBBZBi021Osq4jGAD3AOju5fg==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/node-fetch@2.6.11:
     resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       form-data: 4.0.0
     dev: true
 
@@ -4144,15 +4145,15 @@ packages:
     resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
     dev: true
 
-  /@types/node@20.11.24:
-    resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==}
+  /@types/node@20.11.28:
+    resolution: {integrity: sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==}
     dependencies:
       undici-types: 5.26.5
 
   /@types/nodemailer@6.4.14:
     resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/normalize-package-data@2.4.3:
@@ -4162,13 +4163,13 @@ packages:
   /@types/oauth@0.9.1:
     resolution: {integrity: sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: false
 
   /@types/oauth@0.9.4:
     resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/object-assign-deep@0.4.2:
@@ -4186,7 +4187,7 @@ packages:
   /@types/pg@8.11.2:
     resolution: {integrity: sha512-G2Mjygf2jFMU/9hCaTYxJrwdObdcnuQde1gndooZSOHsNSaCehAuwc7EIuSA34Do8Jx2yZ19KtvW8P0j4EuUXw==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       pg-protocol: 1.6.0
       pg-types: 4.0.1
     dev: true
@@ -4203,7 +4204,7 @@ packages:
     resolution: {integrity: sha512-HVqYj3L+D+S/6qpQRv5qMxrD/5pglzZuhP7ZIqgVSZ+Ck4z1TCFkNIRG8WesFueQTqWFTSgkkAl6f8lwxFPQSw==}
     dependencies:
       '@types/needle': 3.2.0
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/pug@2.0.10:
@@ -4217,7 +4218,7 @@ packages:
   /@types/qrcode@1.5.5:
     resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/qs@6.9.12:
@@ -4241,7 +4242,7 @@ packages:
   /@types/responselike@1.0.0:
     resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/sanitize-html@2.11.0:
     resolution: {integrity: sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==}
@@ -4261,14 +4262,14 @@ packages:
     resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
     dependencies:
       '@types/mime': 1.3.2
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/serve-static@1.15.2:
     resolution: {integrity: sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==}
     dependencies:
       '@types/http-errors': 2.0.1
       '@types/mime': 3.0.1
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/sinonjs__fake-timers@8.1.5:
     resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==}
@@ -4285,7 +4286,7 @@ packages:
   /@types/streamx@2.9.1:
     resolution: {integrity: sha512-9bywzhouyedmci7WCIPFwJ8zASDnxt2gaVUy52X0p0Tt085IJSAEP0L6j4SSNeDMSLzpYu6cPz0GrJZ7kPJ6Bg==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/syslog-pro@1.0.3:
@@ -4315,7 +4316,7 @@ packages:
   /@types/undertaker@1.2.8:
     resolution: {integrity: sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/undertaker-registry': 1.0.1
       async-done: 1.3.2
     dev: true
@@ -4336,7 +4337,7 @@ packages:
     resolution: {integrity: sha512-ctNcmmzbMIKooXjRkyyUCOu2Z4AyqibL+RhXoF3pb7K7j+ezItnakmpm31LymkYHSIM5ey0tjIFzTvFOTSBCGw==}
     dependencies:
       '@types/glob-stream': 8.0.0
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/vinyl': 2.0.7
     dev: true
 
@@ -4344,30 +4345,30 @@ packages:
     resolution: {integrity: sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==}
     dependencies:
       '@types/expect': 1.20.4
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
 
   /@types/web-push@3.6.3:
     resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/websocket@1.0.10:
     resolution: {integrity: sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/ws@8.5.10:
     resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: true
 
   /@types/ws@8.5.5:
     resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
     dev: false
 
   /@types/yargs-parser@21.0.2:
@@ -4408,7 +4409,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.3.3):
+  /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.2):
     resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -4420,23 +4421,23 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.9.1
-      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.2)
       '@typescript-eslint/scope-manager': 5.62.0
-      '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.4.2)
+      '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.2)
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare-lite: 1.4.0
       semver: 7.6.0
-      tsutils: 3.21.0(typescript@5.3.3)
-      typescript: 5.3.3
+      tsutils: 3.21.0(typescript@5.4.2)
+      typescript: 5.4.2
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.3.3):
+  /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.2):
     resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -4448,10 +4449,10 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.9.1
-      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.2)
       '@typescript-eslint/scope-manager': 6.21.0
-      '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.4.2)
+      '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.2)
       '@typescript-eslint/visitor-keys': 6.21.0
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
@@ -4459,8 +4460,8 @@ packages:
       ignore: 5.2.4
       natural-compare: 1.4.0
       semver: 7.6.0
-      ts-api-utils: 1.0.1(typescript@5.3.3)
-      typescript: 5.3.3
+      ts-api-utils: 1.0.1(typescript@5.4.2)
+      typescript: 5.4.2
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -4485,7 +4486,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.3.3):
+  /@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.2):
     resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -4497,15 +4498,15 @@ packages:
     dependencies:
       '@typescript-eslint/scope-manager': 5.62.0
       '@typescript-eslint/types': 5.62.0
-      '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3)
+      '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.2)
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
-      typescript: 5.3.3
+      typescript: 5.4.2
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.3.3):
+  /@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.2):
     resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -4517,11 +4518,11 @@ packages:
     dependencies:
       '@typescript-eslint/scope-manager': 6.21.0
       '@typescript-eslint/types': 6.21.0
-      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3)
+      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.2)
       '@typescript-eslint/visitor-keys': 6.21.0
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
-      typescript: 5.3.3
+      typescript: 5.4.2
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -4562,7 +4563,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.3.3):
+  /@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.2):
     resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -4572,17 +4573,17 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.2)
+      '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.2)
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
-      tsutils: 3.21.0(typescript@5.3.3)
-      typescript: 5.3.3
+      tsutils: 3.21.0(typescript@5.4.2)
+      typescript: 5.4.2
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.3.3):
+  /@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.4.2):
     resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -4592,12 +4593,12 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.2)
+      '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.2)
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
-      ts-api-utils: 1.0.1(typescript@5.3.3)
-      typescript: 5.3.3
+      ts-api-utils: 1.0.1(typescript@5.4.2)
+      typescript: 5.4.2
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -4633,7 +4634,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/typescript-estree@5.62.0(typescript@5.3.3):
+  /@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.2):
     resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -4648,13 +4649,13 @@ packages:
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.6.0
-      tsutils: 3.21.0(typescript@5.3.3)
-      typescript: 5.3.3
+      tsutils: 3.21.0(typescript@5.4.2)
+      typescript: 5.4.2
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3):
+  /@typescript-eslint/typescript-estree@6.21.0(typescript@5.4.2):
     resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -4670,8 +4671,8 @@ packages:
       is-glob: 4.0.3
       minimatch: 9.0.3
       semver: 7.6.0
-      ts-api-utils: 1.0.1(typescript@5.3.3)
-      typescript: 5.3.3
+      ts-api-utils: 1.0.1(typescript@5.4.2)
+      typescript: 5.4.2
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -4696,7 +4697,7 @@ packages:
       - typescript
     dev: true
 
-  /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.3.3):
+  /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.2):
     resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -4707,7 +4708,7 @@ packages:
       '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 5.62.0
       '@typescript-eslint/types': 5.62.0
-      '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3)
+      '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.2)
       eslint: 8.57.0
       eslint-scope: 5.1.1
       semver: 7.6.0
@@ -4716,7 +4717,7 @@ packages:
       - typescript
     dev: true
 
-  /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.3.3):
+  /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.4.2):
     resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -4727,7 +4728,7 @@ packages:
       '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 6.21.0
       '@typescript-eslint/types': 6.21.0
-      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3)
+      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.2)
       eslint: 8.57.0
       semver: 7.6.0
     transitivePeerDependencies:
@@ -4777,15 +4778,15 @@ packages:
       - supports-color
     dev: true
 
-  /@vitejs/plugin-vue@5.0.4(vite@5.1.5)(vue@3.4.21):
+  /@vitejs/plugin-vue@5.0.4(vite@5.1.6)(vue@3.4.21):
     resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==}
     engines: {node: ^18.0.0 || >=20.0.0}
     peerDependencies:
       vite: ^5.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 5.1.5(@types/node@20.11.24)(sass@1.71.1)
-      vue: 3.4.21(typescript@5.3.3)
+      vite: 5.1.6(@types/node@20.11.28)(sass@1.72.0)
+      vue: 3.4.21(typescript@5.4.2)
     dev: true
 
   /@vue/compiler-core@3.4.21:
@@ -4862,7 +4863,7 @@ packages:
     dependencies:
       '@vue/compiler-ssr': 3.4.21
       '@vue/shared': 3.4.21
-      vue: 3.4.21(typescript@5.3.3)
+      vue: 3.4.21(typescript@5.4.2)
     dev: true
 
   /@vue/shared@3.4.21:
@@ -5055,8 +5056,8 @@ packages:
     hasBin: true
     dev: true
 
-  /adm-zip@0.5.10:
-    resolution: {integrity: sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==}
+  /adm-zip@0.5.12:
+    resolution: {integrity: sha512-6TVU49mK6KZb4qG6xWaaM4C7sA/sgUMLy/JYMOzkcp3BvVLpW0fXDFQiIzAuxFCt/2+xD7fNIiPFAoLZPhVNLQ==}
     engines: {node: '>=6.0'}
     dev: false
 
@@ -5234,29 +5235,30 @@ packages:
     resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
     dev: true
 
-  /archiver-utils@5.0.1:
-    resolution: {integrity: sha512-MMAoLdMvT/nckofX1tCLrf7uJce4jTNkiT6smA2u57AOImc1nce7mR3EDujxL5yv6/MnILuQH4sAsPtDS8kTvg==}
+  /archiver-utils@5.0.2:
+    resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==}
     engines: {node: '>= 14'}
     dependencies:
       glob: 10.3.10
       graceful-fs: 4.2.11
+      is-stream: 2.0.1
       lazystream: 1.0.1
       lodash: 4.17.21
       normalize-path: 3.0.0
-      readable-stream: 3.6.2
+      readable-stream: 4.5.2
     dev: false
 
-  /archiver@7.0.0:
-    resolution: {integrity: sha512-R9HM9egs8FfktSqUqyjlKmvF4U+CWNqm/2tlROV+lOFg79MLdT67ae1l3hU47pGy8twSXxHoiefMCh43w0BriQ==}
+  /archiver@7.0.1:
+    resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==}
     engines: {node: '>= 14'}
     dependencies:
-      archiver-utils: 5.0.1
+      archiver-utils: 5.0.2
       async: 3.2.4
       buffer-crc32: 1.0.0
       readable-stream: 4.5.2
       readdir-glob: 1.1.3
       tar-stream: 3.1.7
-      zip-stream: 6.0.0
+      zip-stream: 6.0.1
     dev: false
 
   /archy@1.0.0:
@@ -5644,9 +5646,10 @@ packages:
     resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
     engines: {node: '>= 0.4'}
 
-  /aws-sdk@2.1571.0:
-    resolution: {integrity: sha512-Hixs1aD+7IwsP/Bkb7StFCrOC9ejmw8zBv8xVqEtEughRX6AF8bLKFRoJRbD4V6TrM+gPGpCqoFlpa84HLHkSA==}
+  /aws-sdk@2.1578.0:
+    resolution: {integrity: sha512-8TTN61GbYcvVYnjsboWEzYJhqVC16P7DdZ9jdlpryfZEjmsa8f/Wkg9DZCSWsseHjPIq4G6XeAXN3BJXEpIx4g==}
     engines: {node: '>= 10.0.0'}
+    requiresBuild: true
     dependencies:
       buffer: 4.9.2
       events: 1.1.1
@@ -6176,6 +6179,17 @@ packages:
       function-bind: 1.1.1
       get-intrinsic: 1.2.1
 
+  /call-bind@1.0.7:
+    resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      es-define-property: 1.0.0
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+      get-intrinsic: 1.2.4
+      set-function-length: 1.2.2
+    dev: false
+
   /callsites@3.1.0:
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
     engines: {node: '>=6'}
@@ -6323,14 +6337,14 @@ packages:
       '@kurkle/color': 0.3.2
     dev: true
 
-  /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.2)(date-fns@3.3.1):
+  /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.2)(date-fns@3.5.0):
     resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==}
     peerDependencies:
       chart.js: '>=2.8.0'
       date-fns: '>=2.0.0'
     dependencies:
       chart.js: 4.4.2
-      date-fns: 3.3.1
+      date-fns: 3.5.0
     dev: true
 
   /chartjs-chart-matrix@2.0.1(chart.js@4.4.2):
@@ -6592,7 +6606,7 @@ packages:
     resolution: {integrity: sha512-sX/LQ7LqUhgyaxzbe7IqwPeTr2yfpfUIQ/dgpKo6ZI4y4lpQA0YxAomWIY+7I7rHWcG02PG+OuPREzMW/5tszQ==}
     dependencies:
       inflation: 2.0.0
-      qs: 6.11.2
+      qs: 6.12.0
       raw-body: 2.5.2
       type-is: 1.6.18
     dev: false
@@ -6601,7 +6615,7 @@ packages:
     resolution: {integrity: sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==}
     dependencies:
       inflation: 2.0.0
-      qs: 6.11.2
+      qs: 6.12.0
       raw-body: 2.5.2
       type-is: 1.6.18
     dev: false
@@ -6753,12 +6767,13 @@ packages:
     resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==}
     dev: false
 
-  /compress-commons@6.0.1:
-    resolution: {integrity: sha512-l7occIJn8YwlCEbWUCrG6gPms9qnJTCZSaznCa5HaV+yJMH4kM8BDc7q9NyoQuoiB2O6jKgTcTeY462qw6MyHw==}
+  /compress-commons@6.0.2:
+    resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==}
     engines: {node: '>= 14'}
     dependencies:
       crc-32: 1.2.2
       crc32-stream: 6.0.0
+      is-stream: 2.0.1
       normalize-path: 3.0.0
       readable-stream: 4.5.2
     dev: false
@@ -7088,7 +7103,7 @@ packages:
       - ts-node
     dev: true
 
-  /create-jest@29.7.0(@types/node@20.11.24)(ts-node@10.9.2):
+  /create-jest@29.7.0(@types/node@20.11.28)(ts-node@10.9.2):
     resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -7097,7 +7112,7 @@ packages:
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.11.24)(ts-node@10.9.2)
+      jest-config: 29.7.0(@types/node@20.11.28)(ts-node@10.9.2)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -7271,8 +7286,8 @@ packages:
     engines: {node: '>= 12'}
     dev: false
 
-  /date-fns@3.3.1:
-    resolution: {integrity: sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==}
+  /date-fns@3.5.0:
+    resolution: {integrity: sha512-a+DwyXn7NOfdJireCzAA0B9p7jIXEu/Q9JKCyMYvH6+0vPUNbQceA0neXrdfJ/xzl3mhOh5vibQQ3936Tssm6A==}
 
   /date-time@3.1.0:
     resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==}
@@ -7442,7 +7457,7 @@ packages:
     resolution: {integrity: sha512-c/8x1R0dXPL7NSDdQ94lYPou/A+I6cbo6b7gFb/28HbjcHnKB4RtWXWLgdv7n51GEXL7OE2eoRZQcAu4ZI+vGg==}
     engines: {node: '>=12.0'}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       axios: 1.6.7
       form-data: 3.0.1
       loglevel: 1.9.1
@@ -7476,6 +7491,15 @@ packages:
     resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
     engines: {node: '>=10'}
 
+  /define-data-property@1.1.4:
+    resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      es-define-property: 1.0.0
+      es-errors: 1.3.0
+      gopd: 1.0.1
+    dev: false
+
   /define-properties@1.2.0:
     resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
     engines: {node: '>= 0.4'}
@@ -7851,6 +7875,18 @@ packages:
       which-typed-array: 1.1.11
     dev: true
 
+  /es-define-property@1.0.0:
+    resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      get-intrinsic: 1.2.4
+    dev: false
+
+  /es-errors@1.3.0:
+    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+    engines: {node: '>= 0.4'}
+    dev: false
+
   /es-module-lexer@1.3.0:
     resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==}
     dev: true
@@ -8107,7 +8143,7 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.2)
       debug: 3.2.7
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
@@ -8136,7 +8172,7 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.2)
       debug: 3.2.7
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
@@ -8251,7 +8287,7 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.2)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
@@ -8286,7 +8322,7 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.2)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
@@ -9376,7 +9412,7 @@ packages:
       vue: ^3.0.0
     dependencies:
       focus-trap: 7.5.4
-      vue: 3.4.21(typescript@5.3.3)
+      vue: 3.4.21(typescript@5.4.2)
     dev: true
 
   /focus-trap@7.5.4:
@@ -9465,7 +9501,7 @@ packages:
       dezalgo: 1.0.4
       hexoid: 1.0.0
       once: 1.4.0
-      qs: 6.11.2
+      qs: 6.12.0
     dev: false
 
   /fragment-cache@0.2.1:
@@ -9608,6 +9644,17 @@ packages:
       has-proto: 1.0.1
       has-symbols: 1.0.3
 
+  /get-intrinsic@1.2.4:
+    resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+      has-proto: 1.0.1
+      has-symbols: 1.0.3
+      hasown: 2.0.0
+    dev: false
+
   /get-package-type@0.1.0:
     resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
     engines: {node: '>=8.0.0'}
@@ -9880,8 +9927,8 @@ packages:
       responselike: 2.0.1
     dev: true
 
-  /got@14.2.0:
-    resolution: {integrity: sha512-dBq2KkHcQl3AwPoIWsLsQScCPpUgRulz1qZVthjPYKYOPmYfBnekR3vxecjZbm91Vc3JUGnV9mqFX7B+Fe2quw==}
+  /got@14.2.1:
+    resolution: {integrity: sha512-KOaPMremmsvx6l9BLC04LYE6ZFW4x7e4HkTe3LwBmtuYYQwpeS4XKqzhubTIkaQ1Nr+eXxeori0zuwupXMovBQ==}
     engines: {node: '>=20'}
     dependencies:
       '@sindresorhus/is': 6.1.0
@@ -9949,7 +9996,7 @@ packages:
     resolution: {integrity: sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==}
     engines: {node: '>=10'}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       '@types/vinyl': 2.0.7
       istextorbinary: 3.3.0
       replacestream: 4.0.3
@@ -10003,8 +10050,8 @@ packages:
     engines: {node: '>=0.8.0'}
     dev: true
 
-  /happy-dom@13.6.2:
-    resolution: {integrity: sha512-Ku+wDqcF/KwFA0dI+xIMZd9Jn020RXjuSil/Vz7gu2yhDC3FsDYZ55qqV9k+SGC4opwb4acisXqVSRxUJMlPbQ==}
+  /happy-dom@13.8.6:
+    resolution: {integrity: sha512-Urcv2jvNel19QirWimOwYTW3slpEYGS8PLtzEwAlpTWpnKycXF8s0I7xUBK9fPvWAIc8uZf/CnV2cIwWE8jptw==}
     engines: {node: '>=16.0.0'}
     dependencies:
       entities: 4.5.0
@@ -10046,6 +10093,12 @@ packages:
     dependencies:
       get-intrinsic: 1.2.1
 
+  /has-property-descriptors@1.0.2:
+    resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+    dependencies:
+      es-define-property: 1.0.0
+    dev: false
+
   /has-proto@1.0.1:
     resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
     engines: {node: '>= 0.4'}
@@ -10808,7 +10861,6 @@ packages:
   /is-stream@2.0.1:
     resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
     engines: {node: '>=8'}
-    dev: true
 
   /is-stream@3.0.0:
     resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
@@ -11031,7 +11083,7 @@ packages:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.5.1
@@ -11080,7 +11132,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest-cli@29.7.0(@types/node@20.11.24)(ts-node@10.9.2):
+  /jest-cli@29.7.0(@types/node@20.11.28)(ts-node@10.9.2):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -11094,10 +11146,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.24)(ts-node@10.9.2)
+      create-jest: 29.7.0(@types/node@20.11.28)(ts-node@10.9.2)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.11.24)(ts-node@10.9.2)
+      jest-config: 29.7.0(@types/node@20.11.28)(ts-node@10.9.2)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.7.2
@@ -11148,7 +11200,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-config@29.7.0(@types/node@20.11.24)(ts-node@10.9.2):
+  /jest-config@29.7.0(@types/node@20.11.28)(ts-node@10.9.2):
     resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     peerDependencies:
@@ -11163,7 +11215,7 @@ packages:
       '@babel/core': 7.23.2
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       babel-jest: 29.7.0(@babel/core@7.23.2)
       chalk: 4.1.2
       ci-info: 3.9.0
@@ -11183,7 +11235,7 @@ packages:
       pretty-format: 29.7.0
       slash: 3.0.0
       strip-json-comments: 3.1.1
-      ts-node: 10.9.2(@swc/core@1.4.4)(@types/node@20.11.24)(typescript@5.3.3)
+      ts-node: 10.9.2(@swc/core@1.4.8)(@types/node@20.11.28)(typescript@5.4.2)
     transitivePeerDependencies:
       - babel-plugin-macros
       - supports-color
@@ -11224,7 +11276,7 @@ packages:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       jest-mock: 29.7.0
       jest-util: 29.7.0
     dev: true
@@ -11249,7 +11301,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.8
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -11300,7 +11352,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       jest-util: 29.7.0
     dev: true
 
@@ -11355,7 +11407,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -11386,7 +11438,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       chalk: 4.1.2
       cjs-module-lexer: 1.2.3
       collect-v8-coverage: 1.0.2
@@ -11438,7 +11490,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       chalk: 4.1.2
       ci-info: 3.9.0
       graceful-fs: 4.2.11
@@ -11463,7 +11515,7 @@ packages:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -11482,7 +11534,7 @@ packages:
     resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
     engines: {node: '>= 10.13.0'}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: true
@@ -11491,7 +11543,7 @@ packages:
     resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
@@ -11518,7 +11570,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest@29.7.0(@types/node@20.11.24)(ts-node@10.9.2):
+  /jest@29.7.0(@types/node@20.11.28)(ts-node@10.9.2):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -11531,7 +11583,7 @@ packages:
       '@jest/core': 29.7.0(ts-node@10.9.2)
       '@jest/types': 29.6.3
       import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.11.24)(ts-node@10.9.2)
+      jest-cli: 29.7.0(@types/node@20.11.28)(ts-node@10.9.2)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -12042,8 +12094,8 @@ packages:
       - supports-color
     dev: false
 
-  /koa@2.15.0:
-    resolution: {integrity: sha512-KEL/vU1knsoUvfP4MC4/GthpQrY/p6dzwaaGI6Rt4NQuFqkw3qrvsdYF5pz3wOfi7IGTvMPHC9aZIcUKYFNxsw==}
+  /koa@2.15.1:
+    resolution: {integrity: sha512-kpxzGxsv7tlc0WmccWd6CfdWqYXk4o/FsCTjnKaDnHLjPK/Sy1MpoBkuKO5LN7GdPHgPljrAVmMO3wbFxEJTeA==}
     engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4}
     dependencies:
       accepts: 1.3.8
@@ -13026,8 +13078,8 @@ packages:
   /node-releases@2.0.13:
     resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==}
 
-  /nodemailer@6.9.11:
-    resolution: {integrity: sha512-UiAkgiERuG94kl/3bKfE8o10epvDnl0vokNEtZDPTq9BWzIl6EFT9336SbIT4oaTBD8NmmUTLsQyXHV82eXSWg==}
+  /nodemailer@6.9.12:
+    resolution: {integrity: sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==}
     engines: {node: '>=6.0.0'}
     dev: false
 
@@ -13195,6 +13247,11 @@ packages:
 
   /object-inspect@1.12.3:
     resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
+    dev: true
+
+  /object-inspect@1.13.1:
+    resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
+    dev: false
 
   /object-keys@1.1.1:
     resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
@@ -14505,11 +14562,11 @@ packages:
       yargs: 15.4.1
     dev: false
 
-  /qs@6.11.2:
-    resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==}
+  /qs@6.12.0:
+    resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==}
     engines: {node: '>=0.6'}
     dependencies:
-      side-channel: 1.0.4
+      side-channel: 1.0.6
     dev: false
 
   /query-string@4.3.4:
@@ -15018,26 +15075,26 @@ packages:
       seedrandom: 2.4.2
     dev: false
 
-  /rollup@4.12.0:
-    resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==}
+  /rollup@4.13.0:
+    resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
     dependencies:
       '@types/estree': 1.0.5
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.12.0
-      '@rollup/rollup-android-arm64': 4.12.0
-      '@rollup/rollup-darwin-arm64': 4.12.0
-      '@rollup/rollup-darwin-x64': 4.12.0
-      '@rollup/rollup-linux-arm-gnueabihf': 4.12.0
-      '@rollup/rollup-linux-arm64-gnu': 4.12.0
-      '@rollup/rollup-linux-arm64-musl': 4.12.0
-      '@rollup/rollup-linux-riscv64-gnu': 4.12.0
-      '@rollup/rollup-linux-x64-gnu': 4.12.0
-      '@rollup/rollup-linux-x64-musl': 4.12.0
-      '@rollup/rollup-win32-arm64-msvc': 4.12.0
-      '@rollup/rollup-win32-ia32-msvc': 4.12.0
-      '@rollup/rollup-win32-x64-msvc': 4.12.0
+      '@rollup/rollup-android-arm-eabi': 4.13.0
+      '@rollup/rollup-android-arm64': 4.13.0
+      '@rollup/rollup-darwin-arm64': 4.13.0
+      '@rollup/rollup-darwin-x64': 4.13.0
+      '@rollup/rollup-linux-arm-gnueabihf': 4.13.0
+      '@rollup/rollup-linux-arm64-gnu': 4.13.0
+      '@rollup/rollup-linux-arm64-musl': 4.13.0
+      '@rollup/rollup-linux-riscv64-gnu': 4.13.0
+      '@rollup/rollup-linux-x64-gnu': 4.13.0
+      '@rollup/rollup-linux-x64-musl': 4.13.0
+      '@rollup/rollup-win32-arm64-msvc': 4.13.0
+      '@rollup/rollup-win32-ia32-msvc': 4.13.0
+      '@rollup/rollup-win32-x64-msvc': 4.13.0
       fsevents: 2.3.3
     dev: true
 
@@ -15109,8 +15166,8 @@ packages:
       postcss: 8.4.35
     dev: false
 
-  /sass@1.71.1:
-    resolution: {integrity: sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==}
+  /sass@1.72.0:
+    resolution: {integrity: sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==}
     engines: {node: '>=14.0.0'}
     hasBin: true
     dependencies:
@@ -15206,6 +15263,18 @@ packages:
   /set-blocking@2.0.0:
     resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
 
+  /set-function-length@1.2.2:
+    resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      define-data-property: 1.1.4
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+      get-intrinsic: 1.2.4
+      gopd: 1.0.1
+      has-property-descriptors: 1.0.2
+    dev: false
+
   /set-value@2.0.1:
     resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
     engines: {node: '>=0.10.0'}
@@ -15303,6 +15372,17 @@ packages:
       call-bind: 1.0.2
       get-intrinsic: 1.2.1
       object-inspect: 1.12.3
+    dev: true
+
+  /side-channel@1.0.6:
+    resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      get-intrinsic: 1.2.4
+      object-inspect: 1.13.1
+    dev: false
 
   /signal-exit@3.0.7:
     resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -15896,15 +15976,15 @@ packages:
       whet.extend: 0.9.9
     dev: false
 
-  /swc-loader@0.2.6(@swc/core@1.4.4)(webpack@5.90.3):
+  /swc-loader@0.2.6(@swc/core@1.4.8)(webpack@5.90.3):
     resolution: {integrity: sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==}
     peerDependencies:
       '@swc/core': ^1.2.147
       webpack: '>=2'
     dependencies:
-      '@swc/core': 1.4.4
+      '@swc/core': 1.4.8
       '@swc/counter': 0.1.3
-      webpack: 5.90.3(@swc/core@1.4.4)
+      webpack: 5.90.3(@swc/core@1.4.8)
     dev: true
 
   /swiper@11.0.7:
@@ -15934,8 +16014,8 @@ packages:
       moment: 2.29.4
     dev: false
 
-  /systeminformation@5.22.0:
-    resolution: {integrity: sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==}
+  /systeminformation@5.22.2:
+    resolution: {integrity: sha512-VsXY7i1UFZZhbNRv8QN6yTIqNL66/rXpMGb5xYzXx8jDYyEM0+tQpehpPdaxxvym+oYIHs9usUxDzRDNkabpVQ==}
     engines: {node: '>=8.0.0'}
     os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
     hasBin: true
@@ -15991,7 +16071,7 @@ packages:
     engines: {node: '>=14.16'}
     dev: true
 
-  /terser-webpack-plugin@5.3.10(@swc/core@1.4.4)(webpack@5.90.3):
+  /terser-webpack-plugin@5.3.10(@swc/core@1.4.8)(webpack@5.90.3):
     resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
     engines: {node: '>= 10.13.0'}
     peerDependencies:
@@ -16008,12 +16088,12 @@ packages:
         optional: true
     dependencies:
       '@jridgewell/trace-mapping': 0.3.20
-      '@swc/core': 1.4.4
+      '@swc/core': 1.4.8
       jest-worker: 27.5.1
       schema-utils: 3.3.0
       serialize-javascript: 6.0.1
       terser: 5.27.0
-      webpack: 5.90.3(@swc/core@1.4.4)
+      webpack: 5.90.3(@swc/core@1.4.8)
     dev: true
 
   /terser@5.19.2:
@@ -16245,13 +16325,13 @@ packages:
       escape-string-regexp: 5.0.0
     dev: true
 
-  /ts-api-utils@1.0.1(typescript@5.3.3):
+  /ts-api-utils@1.0.1(typescript@5.4.2):
     resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
     engines: {node: '>=16.13.0'}
     peerDependencies:
       typescript: '>=4.2.0'
     dependencies:
-      typescript: 5.3.3
+      typescript: 5.4.2
     dev: true
 
   /ts-jest@29.1.1(@babel/core@7.23.2)(jest@29.7.0)(typescript@4.9.4):
@@ -16288,7 +16368,7 @@ packages:
       yargs-parser: 21.1.1
     dev: true
 
-  /ts-jest@29.1.2(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.3.3):
+  /ts-jest@29.1.2(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.4.2):
     resolution: {integrity: sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==}
     engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0}
     hasBin: true
@@ -16312,17 +16392,17 @@ packages:
       '@babel/core': 7.23.2
       bs-logger: 0.2.6
       fast-json-stable-stringify: 2.1.0
-      jest: 29.7.0(@types/node@20.11.24)(ts-node@10.9.2)
+      jest: 29.7.0(@types/node@20.11.28)(ts-node@10.9.2)
       jest-util: 29.7.0
       json5: 2.2.3
       lodash.memoize: 4.1.2
       make-error: 1.3.6
       semver: 7.6.0
-      typescript: 5.3.3
+      typescript: 5.4.2
       yargs-parser: 21.1.1
     dev: true
 
-  /ts-loader@9.5.1(typescript@5.3.3)(webpack@5.90.3):
+  /ts-loader@9.5.1(typescript@5.4.2)(webpack@5.90.3):
     resolution: {integrity: sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==}
     engines: {node: '>=12.0.0'}
     peerDependencies:
@@ -16334,11 +16414,11 @@ packages:
       micromatch: 4.0.5
       semver: 7.6.0
       source-map: 0.7.4
-      typescript: 5.3.3
-      webpack: 5.90.3(@swc/core@1.4.4)
+      typescript: 5.4.2
+      webpack: 5.90.3(@swc/core@1.4.8)
     dev: true
 
-  /ts-node@10.9.2(@swc/core@1.4.4)(@types/node@20.11.24)(typescript@5.3.3):
+  /ts-node@10.9.2(@swc/core@1.4.8)(@types/node@20.11.28)(typescript@5.4.2):
     resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
     hasBin: true
     peerDependencies:
@@ -16353,19 +16433,19 @@ packages:
         optional: true
     dependencies:
       '@cspotcode/source-map-support': 0.8.1
-      '@swc/core': 1.4.4
+      '@swc/core': 1.4.8
       '@tsconfig/node10': 1.0.9
       '@tsconfig/node12': 1.0.11
       '@tsconfig/node14': 1.0.3
       '@tsconfig/node16': 1.0.4
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       acorn: 8.10.0
       acorn-walk: 8.2.0
       arg: 4.1.3
       create-require: 1.1.1
       diff: 4.0.2
       make-error: 1.3.6
-      typescript: 5.3.3
+      typescript: 5.4.2
       v8-compile-cache-lib: 3.0.1
       yn: 3.1.1
 
@@ -16436,14 +16516,14 @@ packages:
       typescript: 4.9.4
     dev: true
 
-  /tsutils@3.21.0(typescript@5.3.3):
+  /tsutils@3.21.0(typescript@5.4.2):
     resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
     engines: {node: '>= 6'}
     peerDependencies:
       typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
     dependencies:
       tslib: 1.14.1
-      typescript: 5.3.3
+      typescript: 5.4.2
     dev: true
 
   /tweetnacl@0.14.5:
@@ -16641,7 +16721,7 @@ packages:
       pg: 8.11.3
       reflect-metadata: 0.2.1
       sha.js: 2.4.11
-      ts-node: 10.9.2(@swc/core@1.4.4)(@types/node@20.11.24)(typescript@5.3.3)
+      ts-node: 10.9.2(@swc/core@1.4.8)(@types/node@20.11.28)(typescript@5.4.2)
       tslib: 2.6.1
       uuid: 9.0.1
       yargs: 17.7.2
@@ -16655,8 +16735,8 @@ packages:
     hasBin: true
     dev: true
 
-  /typescript@5.3.3:
-    resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
+  /typescript@5.4.2:
+    resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==}
     engines: {node: '>=14.17'}
     hasBin: true
 
@@ -16985,7 +17065,7 @@ packages:
       replace-ext: 1.0.1
     dev: false
 
-  /vite-plugin-compression@0.5.1(vite@5.1.5):
+  /vite-plugin-compression@0.5.1(vite@5.1.6):
     resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==}
     peerDependencies:
       vite: '>=2.0.0'
@@ -16993,13 +17073,13 @@ packages:
       chalk: 4.1.2
       debug: 4.3.4(supports-color@8.1.1)
       fs-extra: 10.1.0
-      vite: 5.1.5(@types/node@20.11.24)(sass@1.71.1)
+      vite: 5.1.6(@types/node@20.11.28)(sass@1.72.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /vite@5.1.5(@types/node@20.11.24)(sass@1.71.1):
-    resolution: {integrity: sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==}
+  /vite@5.1.6(@types/node@20.11.28)(sass@1.72.0):
+    resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -17026,11 +17106,11 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.11.28
       esbuild: 0.19.5
       postcss: 8.4.35
-      rollup: 4.12.0
-      sass: 1.71.1
+      rollup: 4.13.0
+      sass: 1.72.0
     optionalDependencies:
       fsevents: 2.3.3
     dev: true
@@ -17126,7 +17206,7 @@ packages:
     peerDependencies:
       vue: ^3.0.0
     dependencies:
-      vue: 3.4.21(typescript@5.3.3)
+      vue: 3.4.21(typescript@5.4.2)
     dev: true
 
   /vue@2.7.14:
@@ -17137,7 +17217,7 @@ packages:
       csstype: 3.1.2
     dev: true
 
-  /vue@3.4.21(typescript@5.3.3):
+  /vue@3.4.21(typescript@5.4.2):
     resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
     peerDependencies:
       typescript: '*'
@@ -17150,7 +17230,7 @@ packages:
       '@vue/runtime-dom': 3.4.21
       '@vue/server-renderer': 3.4.21(vue@3.4.21)
       '@vue/shared': 3.4.21
-      typescript: 5.3.3
+      typescript: 5.4.2
     dev: true
 
   /walker@1.0.8:
@@ -17209,7 +17289,7 @@ packages:
     engines: {node: '>=10.13.0'}
     dev: true
 
-  /webpack@5.90.3(@swc/core@1.4.4):
+  /webpack@5.90.3(@swc/core@1.4.8):
     resolution: {integrity: sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==}
     engines: {node: '>=10.13.0'}
     hasBin: true
@@ -17240,7 +17320,7 @@ packages:
       neo-async: 2.6.2
       schema-utils: 3.3.0
       tapable: 2.2.1
-      terser-webpack-plugin: 5.3.10(@swc/core@1.4.4)(webpack@5.90.3)
+      terser-webpack-plugin: 5.3.10(@swc/core@1.4.8)(webpack@5.90.3)
       watchpack: 2.4.0
       webpack-sources: 3.2.3
     transitivePeerDependencies:
@@ -17636,12 +17716,12 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
-  /zip-stream@6.0.0:
-    resolution: {integrity: sha512-X0WFquRRDtL9HR9hc1OrabOP/VKJEX7gAr2geayt3b7dLgXgSXI6ucC4CphLQP/aQt2GyHIYgmXxtC+dVdghAQ==}
+  /zip-stream@6.0.1:
+    resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
     engines: {node: '>= 14'}
     dependencies:
-      archiver-utils: 5.0.1
-      compress-commons: 6.0.1
+      archiver-utils: 5.0.2
+      compress-commons: 6.0.2
       readable-stream: 4.5.2
     dev: false